Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
PhilippMDoerner opened this issue Jan 6, 2024 · 0 comments

Comments

@PhilippMDoerner
Copy link
Contributor

PhilippMDoerner commented Jan 6, 2024

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.

@PhilippMDoerner PhilippMDoerner changed the title Channel.trySend/tryRecv contains a data-race Channel.trySend/tryRecv contains a data-race according to thread-sanitizer Jan 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant