From dac5f09ff75a75d862b321dc581219f68a545437 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Fri, 28 Jan 2022 19:55:44 -0600 Subject: [PATCH] lower nim version dep, add nim version to github actions --- .github/workflows/build.yml | 8 ++ puppy.nimble | 4 +- src/puppy/requestpools.nim | 245 ++++++++++++++++++------------------ tests/test_issue48.nim | 115 ++++++++--------- tests/test_requestpools.nim | 73 +++++------ 5 files changed, 228 insertions(+), 217 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a308be2..f632ecb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,12 +6,20 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] + nim-version: ['1.2.2', '1.2.x', '1.4.x', 'stable'] + include: + - nim-version: '1.4.x' + gc_orc: true + - nim-version: 'stable' + gc_orc: true runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: ${{ matrix.nim-version }} - run: nimble install -y libcurl - run: nimble install -y zippy diff --git a/puppy.nimble b/puppy.nimble index c324479..4263846 100644 --- a/puppy.nimble +++ b/puppy.nimble @@ -1,11 +1,11 @@ -version = "1.5.2" +version = "1.5.3" author = "Andre von Houck" description = "Puppy fetches HTML pages for Nim." license = "MIT" srcDir = "src" -requires "nim >= 1.4.4" +requires "nim >= 1.2.2" requires "urlly >= 0.2.0" requires "libcurl >= 1.0.0" requires "zippy >= 0.7.3" diff --git a/src/puppy/requestpools.nim b/src/puppy/requestpools.nim index 44983d3..e9b20e2 100644 --- a/src/puppy/requestpools.nim +++ b/src/puppy/requestpools.nim @@ -1,144 +1,145 @@ -import puppy, std/locks, std/deques - -when not defined(gcArc): - {.error: "Please use --gc:arc when using Puppy request pools.".} - -type - RequestPoolObj = object - lock: Lock - cond: Cond - threads: seq[Thread[ptr RequestPoolObj]] - queue: Deque[ResponseHandle] - closed: bool - requestsCompleted: bool - - RequestPool* = ref RequestPoolObj - - ResponseHandleInternal = object - lock: Lock - request: Request - response: Response - error: ref PuppyError - refCount: int - - ResponseHandle* = object - internal: ptr ResponseHandleInternal - -proc free(internal: ptr ResponseHandleInternal) = - deinitLock(internal.lock) - internal.request = nil - internal.response = nil - internal.error = nil - dealloc(internal) - -proc `=destroy`*(handle: var ResponseHandle) = - if handle.internal != nil: - acquire(handle.internal.lock) - if handle.internal.refCount == 0: - release(handle.internal.lock) - free handle.internal - else: - dec handle.internal.refCount - release(handle.internal.lock) +when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0): + import puppy, std/locks, std/deques + + when not defined(gcArc): + {.error: "Please use --gc:arc when using Puppy request pools.".} + + type + RequestPoolObj = object + lock: Lock + cond: Cond + threads: seq[Thread[ptr RequestPoolObj]] + queue: Deque[ResponseHandle] + closed: bool + requestsCompleted: bool + + RequestPool* = ref RequestPoolObj + + ResponseHandleInternal = object + lock: Lock + request: Request + response: Response + error: ref PuppyError + refCount: int + + ResponseHandle* = object + internal: ptr ResponseHandleInternal + + proc free(internal: ptr ResponseHandleInternal) = + deinitLock(internal.lock) + internal.request = nil + internal.response = nil + internal.error = nil + dealloc(internal) + + proc `=destroy`*(handle: var ResponseHandle) = + if handle.internal != nil: + acquire(handle.internal.lock) + if handle.internal.refCount == 0: + release(handle.internal.lock) + free handle.internal + else: + dec handle.internal.refCount + release(handle.internal.lock) + + proc `=`*(dst: var ResponseHandle, src: ResponseHandle) = + if src.internal != nil: + acquire(src.internal.lock) + inc src.internal.refCount + release(src.internal.lock) + + if dst.internal != nil: + `=destroy`(dst) + + dst.internal = src.internal + + proc `=destroy`*(pool: var RequestPoolObj) = + acquire(pool.lock) + pool.closed = true + release(pool.lock) -proc `=`*(dst: var ResponseHandle, src: ResponseHandle) = - if src.internal != nil: - acquire(src.internal.lock) - inc src.internal.refCount - release(src.internal.lock) + broadcast(pool.cond) - if dst.internal != nil: - `=destroy`(dst) + joinThreads(pool.threads) - dst.internal = src.internal + deinitLock(pool.lock) + deinitCond(pool.cond) + `=destroy`(pool.threads) + `=destroy`(pool.queue) -proc `=destroy`*(pool: var RequestPoolObj) = - acquire(pool.lock) - pool.closed = true - release(pool.lock) + proc ready*(handle: ResponseHandle): bool = + acquire(handle.internal.lock) + result = handle.internal.response != nil + release(handle.internal.lock) - broadcast(pool.cond) + proc response*(handle: ResponseHandle): Response = + var error: ref PuppyError - joinThreads(pool.threads) + acquire(handle.internal.lock) + result = handle.internal.response + error = handle.internal.error + release(handle.internal.lock) - deinitLock(pool.lock) - deinitCond(pool.cond) - `=destroy`(pool.threads) - `=destroy`(pool.queue) + if error != nil: + raise error -proc ready*(handle: ResponseHandle): bool = - acquire(handle.internal.lock) - result = handle.internal.response != nil - release(handle.internal.lock) + proc workerProc(pool: ptr RequestPoolObj) {.raises: [].} = + while true: + acquire(pool.lock) -proc response*(handle: ResponseHandle): Response = - var error: ref PuppyError + while pool.queue.len == 0 and not pool.closed: + wait(pool.cond, pool.lock) - acquire(handle.internal.lock) - result = handle.internal.response - error = handle.internal.error - release(handle.internal.lock) + if pool.closed: + release(pool.lock) + break - if error != nil: - raise error + let handle = pool.queue.popFirst() -proc workerProc(pool: ptr RequestPoolObj) {.raises: [].} = - while true: - acquire(pool.lock) + release(pool.lock) - while pool.queue.len == 0 and not pool.closed: - wait(pool.cond, pool.lock) + var + response: Response + error: ref PuppyError + try: + response = fetch(handle.internal.request) + except PuppyError as e: + error = e + + acquire(handle.internal.lock) + handle.internal.response = response + handle.internal.error = error + release(handle.internal.lock) - if pool.closed: + acquire(pool.lock) + pool.requestsCompleted = true release(pool.lock) - break - let handle = pool.queue.popFirst() + proc newRequestPool*(maxInFlight: int): RequestPool = + result = RequestPool() + initLock(result.lock) + initCond(result.cond) + result.threads.setLen(maxInFlight) + for thread in result.threads.mitems: + createThread(thread, workerProc, cast[ptr RequestPoolObj](result)) + + proc fetch*(pool: RequestPool, request: Request): ResponseHandle = + result.internal = cast[ptr ResponseHandleInternal]( + alloc0(sizeof(ResponseHandleInternal)) + ) + initLock(result.internal.lock) + result.internal.request = request + acquire(pool.lock) + pool.queue.addLast(result) release(pool.lock) - var - response: Response - error: ref PuppyError - try: - response = fetch(handle.internal.request) - except PuppyError as e: - error = e - - acquire(handle.internal.lock) - handle.internal.response = response - handle.internal.error = error - release(handle.internal.lock) + signal(pool.cond) + proc requestsCompleted*(pool: RequestPool): bool = + ## Returns whether or not at least one request has completed since the last + ## time this proc was called. acquire(pool.lock) - pool.requestsCompleted = true + result = pool.requestsCompleted + pool.requestsCompleted = false release(pool.lock) - -proc newRequestPool*(maxInFlight: int): RequestPool = - result = RequestPool() - initLock(result.lock) - initCond(result.cond) - result.threads.setLen(maxInFlight) - for thread in result.threads.mitems: - createThread(thread, workerProc, cast[ptr RequestPoolObj](result)) - -proc fetch*(pool: RequestPool, request: Request): ResponseHandle = - result.internal = cast[ptr ResponseHandleInternal]( - alloc0(sizeof(ResponseHandleInternal)) - ) - initLock(result.internal.lock) - result.internal.request = request - - acquire(pool.lock) - pool.queue.addLast(result) - release(pool.lock) - - signal(pool.cond) - -proc requestsCompleted*(pool: RequestPool): bool = - ## Returns whether or not at least one request has completed since the last - ## time this proc was called. - acquire(pool.lock) - result = pool.requestsCompleted - pool.requestsCompleted = false - release(pool.lock) diff --git a/tests/test_issue48.nim b/tests/test_issue48.nim index 26f1dd0..99deca1 100644 --- a/tests/test_issue48.nim +++ b/tests/test_issue48.nim @@ -1,67 +1,68 @@ -when not compileOption("threads"): - echo "This test requres --threads:on" -else: - import puppy +when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0): + when not compileOption("threads"): + echo "This test requres --threads:on" + else: + import puppy - type - ChannelRef*[T] = ptr Channel[T] - Client* = ref object - requestThread*: Thread[Client] - action*: ChannelRef[Action] - ActionKind* = enum - Stop, Fetch, - Action* = object - case kind*: ActionKind - of Stop: - discard - of Fetch: - request*: Request + type + ChannelRef*[T] = ptr Channel[T] + Client* = ref object + requestThread*: Thread[Client] + action*: ChannelRef[Action] + ActionKind* = enum + Stop, Fetch, + Action* = object + case kind*: ActionKind + of Stop: + discard + of Fetch: + request*: Request - proc sendAction*(client: Client, action: Action) = - client.action[].send(action) + proc sendAction*(client: Client, action: Action) = + client.action[].send(action) - proc recvAction(client: Client) {.thread.} = - while true: - let action = client.action[].recv() - case action.kind: - of Stop: - break - of Fetch: - discard fetch(action.request) - echo "Fetch success!" + proc recvAction(client: Client) {.thread.} = + while true: + let action = client.action[].recv() + case action.kind: + of Stop: + break + of Fetch: + discard fetch(action.request) + echo "Fetch success!" - proc initShared(client: var Client) = - client.action = cast[ChannelRef[Action]]( - allocShared0(sizeof(Channel[Action])) - ) - client.action[].open() + proc initShared(client: var Client) = + client.action = cast[ChannelRef[Action]]( + allocShared0(sizeof(Channel[Action])) + ) + client.action[].open() - proc deinitShared(client: var Client) = - client.action[].close() - deallocShared(client.action) + proc deinitShared(client: var Client) = + client.action[].close() + deallocShared(client.action) - proc initThreads(client: var Client) = - createThread(client.requestThread, recvAction, client) + proc initThreads(client: var Client) = + createThread(client.requestThread, recvAction, client) - proc deinitThreads(client: var Client) = - client.action[].send(Action(kind: Stop)) - client.requestThread.joinThread() # on puppy 1.4.0, it gets stuck here + proc deinitThreads(client: var Client) = + client.action[].send(Action(kind: Stop)) + client.requestThread.joinThread() # on puppy 1.4.0, it gets stuck here - proc start*(client: var Client) = - initShared(client) - initThreads(client) + proc start*(client: var Client) = + initShared(client) + initThreads(client) - proc stop*(client: var Client) = - deinitThreads(client) - deinitShared(client) - echo "Stopped client thread" + proc stop*(client: var Client) = + deinitThreads(client) + deinitShared(client) + echo "Stopped client thread" - when isMainModule: - var client = Client() - client.start() - let req = Request( - url: parseUrl("https://nim-lang.org/"), - verb: "get", - ) - client.sendAction(Action(kind: Fetch, request: req)) - client.stop() + when isMainModule: + var client = Client() + client.start() + let req = Request( + url: parseUrl("https://nim-lang.org/"), + verb: "get", + ) + client.sendAction(Action(kind: Fetch, request: req)) + client.stop() diff --git a/tests/test_requestpools.nim b/tests/test_requestpools.nim index 4b9f425..abe6bf3 100644 --- a/tests/test_requestpools.nim +++ b/tests/test_requestpools.nim @@ -1,42 +1,43 @@ -when not compileOption("threads"): - echo "This test requres --threads:on" -else: - import std/os, std/locks, puppy, puppy/requestpools, osproc, streams +when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0): + when not compileOption("threads"): + echo "This test requres --threads:on" + else: + import std/os, std/locks, puppy, puppy/requestpools, osproc, streams - var p = startProcess("tests/debug_server", options={poParentStreams}) - sleep(100) + var p = startProcess("tests/debug_server", options={poParentStreams}) + sleep(100) - try: - var pool = newRequestPool(10) + try: + var pool = newRequestPool(10) - for i in 0 ..< 10: - var handles: seq[(int, ResponseHandle)] - for j in 0 ..< 100: - handles.add (j, pool.fetch(Request( - url: parseUrl("http://localhost:8080/page?ret=" & $j), - verb: "get" - ))) + for i in 0 ..< 10: + var handles: seq[(int, ResponseHandle)] + for j in 0 ..< 100: + handles.add (j, pool.fetch(Request( + url: parseUrl("http://localhost:8080/page?ret=" & $j), + verb: "get" + ))) - while true: - var running = 0 - var change = false - for j in countdown(handles.high, 0, 1): - let (id, handle) = handles[j] - if handle.ready: - let r = handle.response - doAssert r.code == 200 - doAssert r.headers.len == 1 - doAssert r.body == $id - handles.del(j) - change = true - else: - inc running + while true: + var running = 0 + var change = false + for j in countdown(handles.high, 0, 1): + let (id, handle) = handles[j] + if handle.ready: + let r = handle.response + doAssert r.code == 200 + doAssert r.headers.len == 1 + doAssert r.body == $id + handles.del(j) + change = true + else: + inc running - if change: - echo "running: ", running, "/", handles.len - if running == 0: - break + if change: + echo "running: ", running, "/", handles.len + if running == 0: + break - sleep(1) - finally: - p.terminate() + sleep(1) + finally: + p.terminate()