nim-works / loony

A high throughput MPMC lock-free queue based on a paper by Giersch, Nolte et al implemented in pure Nim.
https://nim-works.github.io/loony/
MIT License
65 stars 4 forks source link

Ward Segfaults #35

Closed marcgehman closed 4 months ago

marcgehman commented 4 months ago

I 've been trying to use this library via the Ward objects, but I've been running into issues with segfaults.

I tried setting up a consumer thread and a producer thread which each have a ward, and the consumer thread has the queue.

Running each thread individually works, but it seems there's some kind of race condition happening when they both interact which causes a segfault.

import loony
import loony/[Ward]
import std/[os]
type TestRef = ref object

const flags : set[WardFlag] = {PushPausable, Clearable, Pausable}

var queueChannel = Channel[ptr LoonyQueue[TestRef]]()
queueChannel.open()

proc consumerFn(): void =
    let queue = newLoonyQueue[TestRef]()
    var warden = newWard[TestRef](queue, flags)

    # Broadcast the queue address
    queueChannel.send(queue.addr)

    echo "Begin Popping via Consumer Warden"
    while true:
        var res = warden.pop()
        if res == nil:
            echo "Queue is empty."
        else:
            echo "Successfully Popped from queue via Warden."

proc producerFn(): void =
    var queueAddress : ptr LoonyQueue[TestRef] = nil
    # Hold until the queue address is received.
    while queueAddress == nil:
        var (ready, queueAddr) = queueChannel.try_recv()
        if ready:
            echo "Queue Address received"
            queueAddress = queueAddr

    echo "Begin Pushing via Producer Warden"
    let queue : LoonyQueue[TestRef] = cast[LoonyQueue[TestRef]](queueAddress) 
    var warden = newWard[TestRef](queue, flags)
    while true:
        let testRef = new TestRef
        case warden.push(testRef):
            of true:
                echo "Successfully pushed to queue via Warden."
            of false:
                echo "Queue is full."

# Create a consumer thread
var consumerThread: Thread[void]
createThread(consumerThread, consumerFn)

# Create a producer thread
var producerThread: Thread[void]
createThread(producerThread, producerFn)

while true:
    sleep(1000)
    echo "Main Thread is still alive."

joinThread(producerThread)
joinThread(consumerThread)
marcgehman commented 4 months ago

Update: I was using the address directly to create a new queue, instead of dereferencing the queueAddress passed to the producer. Fixing that fixed the issue.

let warden = newWard[TestRef](queueAddress[], flags)

shayanhabibi commented 4 months ago

I would abstain from Wards if possible as they have had next to zero use and are heavily untested.

I made them for Disruptek on request and then he ignored their existence 🤣

Will be happy to help in any way should you have questions.

On Thu, 9 May 2024 at 11:22 AM, marcgehman @.***> wrote:

Update: I was using the address directly to create a new queue, instead of dereferencing the queueAddress passed to the producer. Fixing that fixed the issue.

— Reply to this email directly, view it on GitHub https://github.com/nim-works/loony/issues/35#issuecomment-2101862048, or unsubscribe https://github.com/notifications/unsubscribe-auth/AN2EZW7EZXODKFVMIWK2LCDZBLTXPAVCNFSM6AAAAABHN5FWMWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMBRHA3DEMBUHA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

marcgehman commented 4 months ago

Cool, will definitely take you up on that!