Skip to content

Channel.trySend/tryRecv contains a data-race according to thread-sanitizer #23174

@PhilippMDoerner

Description

@PhilippMDoerner

Description

Heyho, while working to clear up my own package of data-races I noticed that this example will declare a data-race in thread-sanitizer (tsan):

type Thing = ref object

var chan = Channel[Thing]()
chan.open()
var thr: Thread[void]

proc loop() =
  while true:
    let resp = chan.tryRecv()
    if resp.dataAvailable:
      break

proc main() =
  createThread(thr, loop)
  discard chan.trySend(Thing())

  joinThread(thr)
main()

Compiled with nim r --cc:clang --mm:arc -d:release -d:useMalloc -f --passc:"-fsanitize=thread -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer" --debugger:native --passl:"-fsanitize=thread -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer" src/playground.nim

tsan sees a data-race here between trySend with rawSend in one thread and tryRecv with channelReceive in the other. That is because both access the mask property on RawChannel and tryRecv does it before the lock on the channel has been acquired:

proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
                                                  msg: TMsg] =
  var q = cast[PRawChannel](addr(c))
  if q.mask != ChannelDeadMask: # This is the apparently problematic line
    if tryAcquireSys(q.lock):
      if q.count > 0:
        llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
        result.dataAvailable = true
      releaseSys(q.lock)

Even if the data-race does not pose a real problem, I would want to solve it because users of the lib (like me) would want to tsan-check their own applications to make them as stable as can be.

Nim Version

Nim Compiler Version 2.0.2 [Linux: amd64]
Compiled at 2023-12-15
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: c4c44d1
active boot switches: -d:release

Current Output

==================
WARNING: ThreadSanitizer: data race (pid=2330564)
  Write of size 8 at 0x557bc96914f8 by main thread (mutexes: write M0):
    #0 rawSend__system_u5512 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/channels_builtin.nim:327:16 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeb35d) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #1 sendImpl__system_u5552 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/channels_builtin.nim:364:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeb54a) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #2 trySend__playground_u85 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/channels_builtin.nim:383:11 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeb878) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #3 main__playground_u77 /home/philipp/dev/playground/src/playground.nim:15:268 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeb878)
    #4 NimMainModule /home/philipp/dev/playground/src/playground.nim:18:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #5 NimMainInner /home/philipp/dev/playground/src/playground.nim:42:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021)
    #6 NimMain /home/philipp/dev/playground/src/playground.nim:53:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021)
    #7 main /home/philipp/dev/playground/src/playground.nim:61:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021)

  Previous read of size 8 at 0x557bc96914f8 by thread T1:
    #0 tryRecv__playground_u13 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/channels_builtin.nim:419:18 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeb04a) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #1 loop__playground_u12 /home/philipp/dev/playground/src/playground.nim:9:116 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeb709) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #2 threadProcWrapDispatch__stdZtypedthreads_u105 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/threadimpl.nim:66:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeab49) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #3 threadProcWrapStackFrame__stdZtypedthreads_u95 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/threadimpl.nim:95:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeab49)
    #4 threadProcWrapper__stdZtypedthreads_u81 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/threadimpl.nim:101:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xe7375) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)

  Location is global 'chan__playground_u5' of size 152 at 0x557bc96914e0 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0x14974f8)

  Mutex M0 (0x557bc9691510) created at:
    #0 pthread_mutex_init <null> (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0x99f78) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #1 initRawChannel__system_u5469 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/channels_builtin.nim:163:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xe9ca0) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #2 open__playground_u6 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/channels_builtin.nim:447:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xe9ca0)
    #3 NimMainModule /home/philipp/dev/playground/src/playground.nim:4:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec01c) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #4 NimMainInner /home/philipp/dev/playground/src/playground.nim:42:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec01c)
    #5 NimMain /home/philipp/dev/playground/src/playground.nim:53:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec01c)
    #6 main /home/philipp/dev/playground/src/playground.nim:61:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec01c)

  Thread T1 (tid=2330566, running) created by main thread at:
    #0 pthread_create <null> (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0x64a36) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #1 createThread__stdZtypedthreads_u60 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/std/typedthreads.nim:246:103 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xe74a9) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #2 createThread__stdZtypedthreads_u51 /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/std/typedthreads.nim:262:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xe75a0) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #3 main__playground_u77 /home/philipp/dev/playground/src/playground.nim:14:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xeb828) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #4 NimMainModule /home/philipp/dev/playground/src/playground.nim:18:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021) (BuildId: b7cb7edeeb18d9425e27e9a005739638323b0344)
    #5 NimMainInner /home/philipp/dev/playground/src/playground.nim:42:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021)
    #6 NimMain /home/philipp/dev/playground/src/playground.nim:53:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021)
    #7 main /home/philipp/dev/playground/src/playground.nim:61:2 (playground_ADBE1768068E727FD97CC3D798B60A4157F8DA2B+0xec021)

SUMMARY: ThreadSanitizer: data race /home/philipp/.choosenim/toolchains/nim-2.0.2/lib/system/channels_builtin.nim:327:16 in rawSend__system_u5512
==================
ThreadSanitizer: reported 1 warnings

Expected Output

Nothing

Possible Solution

No response

Additional Information

This issue seems to be pretty similar/analogous to the one I raised in the threading repo.

Maybe they have a similar root?

As for why this set-up:

It seems sensible to have a a thread in a while-loop poll a channel for messages dedicated to it. This is a minimal example of such a setup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions