[go] testing/synctest: new package for testing concurrent code

55 views
Skip to first unread message

Damien Neil (Gerrit)

unread,
Aug 27, 2024, 5:00:32 PM8/27/24
to goph...@pubsubhelper.golang.org, Michael Knyszek, Michael Pratt, Go LUCI, Gavin Li, golang-co...@googlegroups.com
Attention needed from Gavin Li, Michael Knyszek and Michael Pratt

Damien Neil added 1 comment

Patchset-level comments
File-level comment, Patchset 21 (Latest):
Damien Neil . resolved

Thanks for offering to review!

Open in Gerrit

Related details

Attention is currently required from:
  • Gavin Li
  • Michael Knyszek
  • Michael Pratt
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
Gerrit-Change-Number: 591997
Gerrit-PatchSet: 21
Gerrit-Owner: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
Gerrit-CC: Gavin Li <gfl...@gmail.com>
Gerrit-Attention: Michael Knyszek <mkny...@google.com>
Gerrit-Attention: Michael Pratt <mpr...@google.com>
Gerrit-Attention: Gavin Li <gfl...@gmail.com>
Gerrit-Comment-Date: Tue, 27 Aug 2024 21:00:26 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
unsatisfied_requirement
satisfied_requirement
open
diffy

Damien Neil (Gerrit)

unread,
Aug 27, 2024, 5:39:56 PM8/27/24
to goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, Michael Pratt, Gavin Li, golang-co...@googlegroups.com
Attention needed from Gavin Li, Michael Knyszek and Michael Pratt

Damien Neil voted Commit-Queue+1

Commit-Queue+1
Gerrit-Comment-Date: Tue, 27 Aug 2024 21:39:50 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Michael Pratt (Gerrit)

unread,
Sep 3, 2024, 5:02:05 PM9/3/24
to Damien Neil, goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, Michael Pratt, Gavin Li, golang-co...@googlegroups.com
Attention needed from Damien Neil, Gavin Li and Michael Knyszek

Michael Pratt added 13 comments

Patchset-level comments
Michael Pratt . resolved

I haven't reviewed everything yet, but want to send out what I've got so far.

In particular, see my comment on L17 of synctest.go. I don't currently understand how to use this API safely with globals containing mutexes (including some std API examples).

File src/internal/synctest/synctest.go
Line 15, Patchset 21 (Latest):// Time advances when every goroutine is idle.
Michael Pratt . unresolved

I assume this means that time.Sleep blocks until all other goroutines are idle? That may be worth calling out here?

Line 16, Patchset 21 (Latest):// If every goroutine is idle and there are no timers scheduled,
// Run panics.
Michael Pratt . unresolved

Won't this be flaky under certain use-cases?

e.g., if one of the goroutines attempts to lock a sync.Mutex from outside of the group (e.g, `crypto/rand.Reader` is a global with a mutex; `reflect.FuncOf` has a global mutex protecting a cache) and there is contention causing the goroutine to block, that will make the goroutine "idle" and thus the group eligible to panic even though it will presumably still make progress.

Line 19, Patchset 21 (Latest)://go:linkname Run
Michael Pratt . resolved

I can never remember our preferred linkname form, so upon seeing this I went off and wrote some docs in CL 609715.

Apologies my tangent delayed my review! (You are doing everything correct here, nothing to do)

Line 24, Patchset 21 (Latest):// is idle.
Michael Pratt . resolved

micronit: accidental newline? Or intentional to put a newline after each comma?

Line 34, Patchset 21 (Latest):// A goroutine executing a system call is never idle,
Michael Pratt . unresolved

Does blocking in netpoll count as idle? (e.g., `os.File.Read` on a pipe)

I can't tell from the text here. The `os.File.Read` feels like a blocking system call like you describe here, but I know it is more complex under the hood.

File src/internal/synctest/synctest_test.go
Line 77, Patchset 21 (Latest):// TestGC tests that GC workers created by a goroutine in a synctest group
Michael Pratt . resolved

While you are testing really subtle cases, a test that the finalizer goroutine isn't affected would be nice (see `runtime.createfing`).

Line 77, Patchset 21 (Latest):// TestGC tests that GC workers created by a goroutine in a synctest group
Michael Pratt . resolved

The GC workers are created when the first GC starts. If a GC has already run prior to starting this test then you won't get the coverage you want.

To make this robust I think you'd need to run it in a subprocess where you can fail if a GC starts too soon. I don't know if that is worth the complexity.

Line 105, Patchset 21 (Latest): runtime.ReadMemStats(&mb)
Michael Pratt . resolved

Consider reading `/gc/cycles/total:gc-cycles` from runtime/metrics instead.

ReadMemStats has to STW, which perturbs the scheduler a lot. I don't see a specific problem with STW, but it would make this test harder to reason about if there was a CI failure.

File src/runtime/synctest.go
Line 73, Patchset 21 (Latest): case waitReasonChanReceiveNilChan:
Michael Pratt . unresolved

It would be nice to have a complete enumeration of all wait reasons with a build or init time check that we haven't forgotten any. It feels too easy to add a new wait reason without thinking it add it here.

Line 112, Patchset 21 (Latest): goready(wake, 0)
Michael Pratt . unresolved

It is invalid to ready a goroutine if it isn't waiting. IIUC, `active` should protect us from readying the root while it is running (since `active > 0` while root runs). If that is correct, could you note this somewhere?

Line 184, Patchset 21 (Latest): sg.now = next
Michael Pratt . resolved

The semantics of time inside of synctest are pretty similar to faketime used by the playground (time_fake.go). It would be nice if we could have a single fake time implementation shared by both modes.

Marking as resolved because I don't see an obvious way to do this, but it is worth thinking about.

Line 191, Patchset 21 (Latest): }
Michael Pratt . unresolved

Perhaps check that `gp.timer.isFake` is false and throw otherwise?

`timeSleep` initializes `gp.timer` with `isFake` if the goroutine is in a group. Nothing ever clears `isFake`.

That is fine for most goroutines as they are in the group for their entire lifetime, but the root goroutine joins the group only for the duration of this function.

If something in this function were to call `timeSleep`, it could permanently poison this goroutine's sleep timer. I don't see any reason that we would do that, but it would be a nasty bug.

Open in Gerrit

Related details

Attention is currently required from:
  • Damien Neil
  • Gavin Li
  • Michael Knyszek
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
    Gerrit-Change-Number: 591997
    Gerrit-PatchSet: 21
    Gerrit-Owner: Damien Neil <dn...@google.com>
    Gerrit-Reviewer: Damien Neil <dn...@google.com>
    Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
    Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
    Gerrit-CC: Gavin Li <gfl...@gmail.com>
    Gerrit-Attention: Michael Knyszek <mkny...@google.com>
    Gerrit-Attention: Damien Neil <dn...@google.com>
    Gerrit-Attention: Gavin Li <gfl...@gmail.com>
    Gerrit-Comment-Date: Tue, 03 Sep 2024 21:01:59 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Damien Neil (Gerrit)

    unread,
    Sep 3, 2024, 6:17:06 PM9/3/24
    to goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, Michael Pratt, Gavin Li, golang-co...@googlegroups.com
    Attention needed from Gavin Li, Michael Knyszek and Michael Pratt

    Damien Neil added 5 comments

    Patchset-level comments
    Damien Neil . resolved

    Thanks! Just responded to the high-level points so far. The point about reflect.FuncOf is a very good one, and I'm not certain yet what the answer (if any) is.

    File src/internal/synctest/synctest.go
    Line 15, Patchset 21 (Latest):// Time advances when every goroutine is idle.
    Michael Pratt . unresolved

    I assume this means that time.Sleep blocks until all other goroutines are idle? That may be worth calling out here?

    Damien Neil

    Any suggestions on how to phrase that? "Sleep blocks until all other goroutines are idle" is *almost* correct, but if two goroutines sleep until the same instant then both wake at the same time.

    My thought had been to include some examples in the public version of the package to demonstrate how Sleep interacts with other goroutines.

    Line 16, Patchset 21 (Latest):// If every goroutine is idle and there are no timers scheduled,
    // Run panics.
    Michael Pratt . unresolved

    Won't this be flaky under certain use-cases?

    e.g., if one of the goroutines attempts to lock a sync.Mutex from outside of the group (e.g, `crypto/rand.Reader` is a global with a mutex; `reflect.FuncOf` has a global mutex protecting a cache) and there is contention causing the goroutine to block, that will make the goroutine "idle" and thus the group eligible to panic even though it will presumably still make progress.

    Damien Neil

    I hadn't considered the `crypto/rand.Reader` or `reflect.FuncOf` cases. This is an interesting problem. (In the sense of "may you live in interesting times", I fear.)

    For more visibility, I've commented further on: https://github.com/golang/go/issues/67434#issuecomment-2327535500

    Michael Pratt . resolved

    micronit: accidental newline? Or intentional to put a newline after each comma?

    Damien Neil

    Intentional, following the "new line after every new concept" style, but I could merge it if you want.

    Line 34, Patchset 21 (Latest):// A goroutine executing a system call is never idle,
    Michael Pratt . resolved

    Does blocking in netpoll count as idle? (e.g., `os.File.Read` on a pipe)

    I can't tell from the text here. The `os.File.Read` feels like a blocking system call like you describe here, but I know it is more complex under the hood.

    Damien Neil

    Blocking in netpoll does not count as idle. Reworded a bit to try to make this clearer.

    Treating a goroutine blocked on a read from a pipe or loopback connection as idle is problematic, because we can't distinguish between "durably blocked until this test proceeds and data is written to the pipe" and "the kernel is about to tell us data is available, but we haven't read the notification yet". So instead we consider anything that touches the OS non-idle. The means tests that work on network connections will need to use an in-process fake, like net.Pipe.

    (net.Pipe's unbuffered nature makes it a poor fake for some cases, but that's a separate issue.)

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Gavin Li
    • Michael Knyszek
    • Michael Pratt
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
    Gerrit-Change-Number: 591997
    Gerrit-PatchSet: 21
    Gerrit-Owner: Damien Neil <dn...@google.com>
    Gerrit-Reviewer: Damien Neil <dn...@google.com>
    Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
    Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
    Gerrit-CC: Gavin Li <gfl...@gmail.com>
    Gerrit-Attention: Michael Knyszek <mkny...@google.com>
    Gerrit-Attention: Michael Pratt <mpr...@google.com>
    Gerrit-Attention: Gavin Li <gfl...@gmail.com>
    Gerrit-Comment-Date: Tue, 03 Sep 2024 22:16:56 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Michael Pratt <mpr...@google.com>
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Damien Neil (Gerrit)

    unread,
    Sep 10, 2024, 2:17:39 PM9/10/24
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Gavin Li, Michael Knyszek and Michael Pratt

    Damien Neil uploaded new patchset

    Damien Neil uploaded patch set #22 to this change.
    Following approvals got outdated and were removed:
    • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Gavin Li
    • Michael Knyszek
    • Michael Pratt
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
      Gerrit-Change-Number: 591997
      Gerrit-PatchSet: 22
      unsatisfied_requirement
      open
      diffy

      Damien Neil (Gerrit)

      unread,
      Sep 10, 2024, 2:20:47 PM9/10/24
      to goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, Michael Pratt, Gavin Li, golang-co...@googlegroups.com
      Attention needed from Gavin Li, Michael Knyszek and Michael Pratt

      Damien Neil added 8 comments

      File src/internal/synctest/synctest.go
      Line 15, Patchset 21:// Time advances when every goroutine is idle.
      Michael Pratt . resolved

      I assume this means that time.Sleep blocks until all other goroutines are idle? That may be worth calling out here?

      Damien Neil

      Any suggestions on how to phrase that? "Sleep blocks until all other goroutines are idle" is *almost* correct, but if two goroutines sleep until the same instant then both wake at the same time.

      My thought had been to include some examples in the public version of the package to demonstrate how Sleep interacts with other goroutines.

      Damien Neil

      Attempted a clarification here; PTAL.

      Line 16, Patchset 21:// If every goroutine is idle and there are no timers scheduled,
      // Run panics.
      Michael Pratt . resolved

      Won't this be flaky under certain use-cases?

      e.g., if one of the goroutines attempts to lock a sync.Mutex from outside of the group (e.g, `crypto/rand.Reader` is a global with a mutex; `reflect.FuncOf` has a global mutex protecting a cache) and there is contention causing the goroutine to block, that will make the goroutine "idle" and thus the group eligible to panic even though it will presumably still make progress.

      Damien Neil

      I hadn't considered the `crypto/rand.Reader` or `reflect.FuncOf` cases. This is an interesting problem. (In the sense of "may you live in interesting times", I fear.)

      For more visibility, I've commented further on: https://github.com/golang/go/issues/67434#issuecomment-2327535500

      Damien Neil

      I've adjusted the idleness rules:

      • Mutexes no longer count for idleness.
      • Channels are associated with bubbles. A bubbled goroutine is idle when blocked on a bubbled channel, but not idle when blocked on a non-bubbled channel.
      • Operating on a bubbled channel from outside the bubble panics.
      File src/internal/synctest/synctest_test.go
      Line 77, Patchset 21:// TestGC tests that GC workers created by a goroutine in a synctest group
      Michael Pratt . resolved

      The GC workers are created when the first GC starts. If a GC has already run prior to starting this test then you won't get the coverage you want.

      To make this robust I think you'd need to run it in a subprocess where you can fail if a GC starts too soon. I don't know if that is worth the complexity.

      Damien Neil

      Moved this to the runtime runTestProg test.

      Line 77, Patchset 21:// TestGC tests that GC workers created by a goroutine in a synctest group
      Michael Pratt . resolved

      While you are testing really subtle cases, a test that the finalizer goroutine isn't affected would be nice (see `runtime.createfing`).

      Damien Neil

      Done.

      This goes into an almost-no-dependencies runTestProg test in the runtime package, since otherwise the os package creates the finalizer goroutine before us.

      Line 105, Patchset 21: runtime.ReadMemStats(&mb)
      Michael Pratt . resolved

      Consider reading `/gc/cycles/total:gc-cycles` from runtime/metrics instead.

      ReadMemStats has to STW, which perturbs the scheduler a lot. I don't see a specific problem with STW, but it would make this test harder to reason about if there was a CI failure.

      Damien Neil

      Done.

      File src/runtime/synctest.go
      Line 73, Patchset 21: case waitReasonChanReceiveNilChan:
      Michael Pratt . resolved

      It would be nice to have a complete enumeration of all wait reasons with a build or init time check that we haven't forgotten any. It feels too easy to add a new wait reason without thinking it add it here.

      Damien Neil

      Moved the list of reasons to runtime2.go, next to existing enumerations of reasons.

      I think it's unlikely that we'd accidentally add a new reason which should be considered idle; synctest is conservative about when a goroutine becomes idle, and the default is always going to be non-idle.

      Line 112, Patchset 21: goready(wake, 0)
      Michael Pratt . resolved

      It is invalid to ready a goroutine if it isn't waiting. IIUC, `active` should protect us from readying the root while it is running (since `active > 0` while root runs). If that is correct, could you note this somewhere?

      Damien Neil

      Added a comment to `maybeWakeLocked`.

      Line 191, Patchset 21: }
      Michael Pratt . resolved

      Perhaps check that `gp.timer.isFake` is false and throw otherwise?

      `timeSleep` initializes `gp.timer` with `isFake` if the goroutine is in a group. Nothing ever clears `isFake`.

      That is fine for most goroutines as they are in the group for their entire lifetime, but the root goroutine joins the group only for the duration of this function.

      If something in this function were to call `timeSleep`, it could permanently poison this goroutine's sleep timer. I don't see any reason that we would do that, but it would be a nasty bug.

      Damien Neil

      Done

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Gavin Li
      • Michael Knyszek
      • Michael Pratt
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
        Gerrit-Change-Number: 591997
        Gerrit-PatchSet: 22
        Gerrit-Owner: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
        Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
        Gerrit-CC: Gavin Li <gfl...@gmail.com>
        Gerrit-Attention: Michael Knyszek <mkny...@google.com>
        Gerrit-Attention: Michael Pratt <mpr...@google.com>
        Gerrit-Attention: Gavin Li <gfl...@gmail.com>
        Gerrit-Comment-Date: Tue, 10 Sep 2024 18:20:41 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Michael Pratt <mpr...@google.com>
        Comment-In-Reply-To: Damien Neil <dn...@google.com>
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Damien Neil (Gerrit)

        unread,
        Sep 10, 2024, 4:54:26 PM9/10/24
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Damien Neil, Gavin Li, Michael Knyszek and Michael Pratt

        Damien Neil uploaded new patchset

        Damien Neil uploaded patch set #23 to this change.
        Following approvals got outdated and were removed:
        • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Damien Neil
        • Gavin Li
        • Michael Knyszek
        • Michael Pratt
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: newpatchset
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
        Gerrit-Change-Number: 591997
        Gerrit-PatchSet: 23
        Gerrit-Owner: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
        Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
        Gerrit-CC: Gavin Li <gfl...@gmail.com>
        Gerrit-Attention: Michael Knyszek <mkny...@google.com>
        Gerrit-Attention: Michael Pratt <mpr...@google.com>
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Damien Neil (Gerrit)

        unread,
        Sep 10, 2024, 6:55:53 PM9/10/24
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Damien Neil, Gavin Li, Michael Knyszek and Michael Pratt

        Damien Neil uploaded new patchset

        Damien Neil uploaded patch set #24 to this change.
        Following approvals got outdated and were removed:
        • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Damien Neil
        • Gavin Li
        • Michael Knyszek
        • Michael Pratt
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: newpatchset
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
        Gerrit-Change-Number: 591997
        Gerrit-PatchSet: 24
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Damien Neil (Gerrit)

        unread,
        Sep 27, 2024, 2:34:19 PM9/27/24
        to goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, Michael Pratt, Gavin Li, golang-co...@googlegroups.com
        Attention needed from Gavin Li, Michael Knyszek and Michael Pratt

        Damien Neil added 1 comment

        Patchset-level comments
        File-level comment, Patchset 24 (Latest):
        Damien Neil . resolved

        I think the current version of the CL matches the version of the API we've decided to go ahead with, and should be ready for review.

        Our plan is now to initially release the testing/synctest package behind a GOEXPERIMENT. I'll add the GOEXPERIMENT-guarded external package in a followup to this CL as a thin wrapper around the internal one.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Gavin Li
        • Michael Knyszek
        • Michael Pratt
        Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
          Gerrit-Change-Number: 591997
          Gerrit-PatchSet: 24
          Gerrit-Owner: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
          Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
          Gerrit-CC: Gavin Li <gfl...@gmail.com>
          Gerrit-Attention: Michael Knyszek <mkny...@google.com>
          Gerrit-Attention: Michael Pratt <mpr...@google.com>
          Gerrit-Attention: Gavin Li <gfl...@gmail.com>
          Gerrit-Comment-Date: Fri, 27 Sep 2024 18:34:12 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          unsatisfied_requirement
          satisfied_requirement
          open
          diffy

          Michael Pratt (Gerrit)

          unread,
          Nov 18, 2024, 12:40:29 PM11/18/24
          to Damien Neil, goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, Michael Pratt, Gavin Li, golang-co...@googlegroups.com
          Attention needed from Damien Neil, Gavin Li and Michael Knyszek

          Michael Pratt added 27 comments

          File src/internal/synctest/synctest.go
          Line 36, Patchset 24 (Latest):// Wait blocks until every goroutine within the current bubble,
          Michael Pratt . resolved

          IMO, it would be worth stating that Wait panics if called from a non-bubbled goroutine.

          Line 47, Patchset 24 (Latest):// For example, a goroutine blocked reading from an network connection
          // is not idle, even if no data is currently available on the connection.
          //
          // A goroutine is not idle when blocked on a send or receive on a channel
          // that was not created within its bubble.
          Michael Pratt . resolved

          Doesn't need to be done now, but if this becomes a public package I think these need a "why?" explanation because this definition seems odd without the background context.

          File src/internal/synctest/synctest_test.go
          Line 117, Patchset 24 (Latest): <-tm.C
          Michael Pratt . resolved

          Oh no, I hope no global APIs do this. (Luckily this seems fairly niche)

          Line 233, Patchset 24 (Latest): <-readyc
          Michael Pratt . unresolved

          Nothing ever sends to or closes readyc. What is the purpose of this timer?

          File src/runtime/chan.go
          Line 38, Patchset 24 (Latest): synctest bool // true if created in a synctest bubble
          Michael Pratt . resolved

          Can you remind me why we don't track the specific bubble? Is it just to save space?

          Line 901, Patchset 24 (Latest): // We don't want the goroutine's synctest group to become idle,
          Michael Pratt . resolved

          Why would this happen? Won't we ready the goroutine before our's parks? I assume I'm missing something here.

          File src/runtime/coro.go
          Line 240, Patchset 24 (Latest): sg.decActive()
          Michael Pratt . resolved

          Does this mean that a coroutine that is not executing because it is waiting on the next iteration is considered idle by synctest?

          File src/runtime/malloc.go
          Line 995, Patchset 24 (Latest): // but avoids any contamination between GC assist and synctest.
          Michael Pratt . unresolved

          Is this just about assists? If so, IMO it makes more sense in gcAssistAlloc.

          e.g., newUserArenaChunk (for GOEXPERIMENT=arena) can also assist but is missing this change.

          File src/runtime/mgc.go
          Line 621, Patchset 24 (Latest): // Mark the group as active even if we block somewhere below.
          Michael Pratt . unresolved

          gcBgMarkStartWorkers starts new goroutines. What prevents them from starting in the bubble? (Edit: isSystemGoroutine in newproc)

          It might be better to drop the syncGroup during this function like mallocgc does.

          File src/runtime/mgcmark.go
          Line 426, Patchset 24 (Latest): // Mark the group as active even if we block somewhere below.
          Michael Pratt . unresolved

          Why is this necessary given mallocgc drops the syncGroup?

          File src/runtime/proc.go
          Line 1462, Patchset 24 (Latest): gp.syncGroup.incActive()
          Michael Pratt . unresolved

          Why?

          File src/runtime/select.go
          Line 168, Patchset 24 (Latest): nonsynctest := false
          Michael Pratt . resolved

          nit: multiple negatives are a little confusing. Perhaps invert this and call it allSynctest.

          File src/runtime/sema.go
          Line 623, Patchset 24 (Latest): throw("semaphore wake of synctest goroutine from outside bubble")
          Michael Pratt . resolved

          Print the id of both synctest groups? You'll thank me later.

          File src/runtime/sizeof_test.go
          Line 23, Patchset 24 (Latest): {runtime.G{}, 276, 448}, // g, but exported for testing
          Michael Pratt . resolved

          Why 16 bytes?

          File src/runtime/synctest.go
          Line 17, Patchset 24 (Latest): waiter *g // caller of synctest.Wait
          Michael Pratt . unresolved

          What if two goroutines call Wait?

          (That seems fine semantically (though maybe confusing?), they both wake up when everything else is idle.)

          Edit: I see below that this panics. The Wait documentation should say so.

          Line 21, Patchset 24 (Latest): active int // group will not idle when active > 0
          Michael Pratt . unresolved

          The group will not idle when active > 0 or running > 0.

          Perhaps expand upon this in the comments. I was initially under the impression that active covered all reasons for the group to be non-idle, but it turns out it only covers reasons other than running goroutines. It would be nice to have a breakdown of what both of these fields cover.

          Line 54, Patchset 24 (Latest): racereleasemergeg(gp, sg.raceaddr())
          Michael Pratt . resolved

          Are the race semantics documented in a comment somewhere?

          Line 100, Patchset 24 (Latest): sg.active++
          Michael Pratt . resolved

          Why is this necessary given that the sg.waiter/sg.root will be runnable soon (L89)? Is it just because we can't call goready with sg.mu held?

          If so, perhaps say so explicitly in the comment. My read of changegstatus above is that runnable goroutines do count towards running.

          Line 106, Patchset 24 (Latest): // active > 0 while root runs, so we know it isn't running now.
          Michael Pratt . unresolved

          Not true. Root only increments active on L144, _after_ creating a goroutine for f (L140). That goroutine could exit/block before root ever increments active.

          But the root does have running > 0 at that point, so it should be OK.

          Line 144, Patchset 24 (Latest): sg.active++
          Michael Pratt . resolved

          What is the advantage to incrementing active vs marking waitReasonSynctestRun as non-idle?

          Line 228, Patchset 24 (Latest): if gp.syncGroup.running == 0 && gp.syncGroup.active == 0 {
          Michael Pratt . unresolved

          IIUC, in this case, maybeWakeLocked will have woken root, as maybeWakeLocked looks at sg.waiter, not sg.waiting.

          This seems like the wrong semantics, as the waiter and root will run at the same time.

          Should maybeWakeLocked be looking at sg.waiting, and if sg.waiter is nil just do nothing with the knowledge that this function will shortly run the waiter?

          File src/runtime/time.go
          Line 17, Patchset 24 (Latest):func timeNow() (sec int64, nsec int32, mono int64) {
          Michael Pratt . unresolved

          Since these are pushed, I think typical naming would be time.now (in time) and time_now (in runtime).

          Same below (time.nano, time_nano).

          Line 21, Patchset 24 (Latest): return sec, nsec, 0
          Michael Pratt . resolved

          I wonder if any programs will be broken by the lack of monotonic time. I suppose not since the fake time is monotonic anyway? Still, why not just return sg.now here?

          Line 1091, Patchset 24 (Latest): if gp.syncGroup != nil {
          Michael Pratt . resolved

          Do I understand correctly that we expect this to always be false because we expect to always be on a g0?

          (Channel timers can't get here in synctest mode)

          Line 1327, Patchset 24 (Latest): return
          Michael Pratt . resolved

          Comment no need to do anything because root will do it?

          File src/runtime/time_linux_amd64.s
          Line 14, Patchset 24 (Latest):TEXT time·now<ABIInternal>(SB),NOSPLIT,$16-24
          Michael Pratt . unresolved

          IMO, this should be renamed to something in runtime now that it is unused by the time package.

          File src/time/time.go
          Line 1303, Patchset 24 (Latest):func now() (sec int64, nsec int32, mono int64)
          Michael Pratt . unresolved

          This has no callers, remove?

          Open in Gerrit

          Related details

          Attention is currently required from:
          • Damien Neil
          • Gavin Li
          • Michael Knyszek
          Submit Requirements:
            • requirement is not satisfiedCode-Review
            • requirement is not satisfiedNo-Unresolved-Comments
            • requirement is not satisfiedReview-Enforcement
            • requirement satisfiedTryBots-Pass
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: comment
            Gerrit-Project: go
            Gerrit-Branch: master
            Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
            Gerrit-Change-Number: 591997
            Gerrit-PatchSet: 24
            Gerrit-Owner: Damien Neil <dn...@google.com>
            Gerrit-Reviewer: Damien Neil <dn...@google.com>
            Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
            Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
            Gerrit-CC: Gavin Li <gfl...@gmail.com>
            Gerrit-Attention: Michael Knyszek <mkny...@google.com>
            Gerrit-Attention: Damien Neil <dn...@google.com>
            Gerrit-Attention: Gavin Li <gfl...@gmail.com>
            Gerrit-Comment-Date: Mon, 18 Nov 2024 17:40:22 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            unsatisfied_requirement
            satisfied_requirement
            open
            diffy

            Damien Neil (Gerrit)

            unread,
            Nov 18, 2024, 5:30:12 PM11/18/24
            to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
            Attention needed from Damien Neil, Gavin Li and Michael Knyszek

            Damien Neil uploaded new patchset

            Damien Neil uploaded patch set #25 to this change.
            Following approvals got outdated and were removed:
            • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
            Open in Gerrit

            Related details

            Attention is currently required from:
            • Damien Neil
            • Gavin Li
            • Michael Knyszek
            Submit Requirements:
              • requirement is not satisfiedCode-Review
              • requirement is not satisfiedNo-Unresolved-Comments
              • requirement is not satisfiedReview-Enforcement
              • requirement is not satisfiedTryBots-Pass
              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
              Gerrit-MessageType: newpatchset
              Gerrit-Project: go
              Gerrit-Branch: master
              Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
              Gerrit-Change-Number: 591997
              Gerrit-PatchSet: 25
              unsatisfied_requirement
              open
              diffy

              Damien Neil (Gerrit)

              unread,
              Nov 18, 2024, 5:30:41 PM11/18/24
              to goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, Michael Pratt, Gavin Li, golang-co...@googlegroups.com
              Attention needed from Gavin Li, Michael Knyszek and Michael Pratt

              Damien Neil added 27 comments

              Patchset-level comments
              File-level comment, Patchset 25 (Latest):
              Damien Neil . resolved

              Thank you for a comprehensive and useful review! That was very helpful.

              File src/internal/synctest/synctest.go
              Line 36, Patchset 24:// Wait blocks until every goroutine within the current bubble,
              Michael Pratt . resolved

              IMO, it would be worth stating that Wait panics if called from a non-bubbled goroutine.

              Damien Neil

              Done.

              Line 47, Patchset 24:// For example, a goroutine blocked reading from an network connection

              // is not idle, even if no data is currently available on the connection.
              //
              // A goroutine is not idle when blocked on a send or receive on a channel
              // that was not created within its bubble.
              Michael Pratt . resolved

              Doesn't need to be done now, but if this becomes a public package I think these need a "why?" explanation because this definition seems odd without the background context.

              Damien Neil

              Reworded the docs to change "idle" to "durably blocked", and specifically defined "durably blocked" as blocked in a way that can only be unblocked by another goroutine in the bubble.

              File src/internal/synctest/synctest_test.go
              Line 233, Patchset 24: <-readyc
              Michael Pratt . resolved

              Nothing ever sends to or closes readyc. What is the purpose of this timer?

              Damien Neil

              I have no idea. Stale test code that snuck in, I think. Removed.

              File src/runtime/chan.go
              Line 38, Patchset 24: synctest bool // true if created in a synctest bubble
              Michael Pratt . resolved

              Can you remind me why we don't track the specific bubble? Is it just to save space?

              Damien Neil

              Just to save space. A bool here doesn't change the size of hchan. I don't want to increase the size of every production program's channels for a test-only feature.

              If we want to be more robust about catching cross-bubble operations, we could have a global map of chan->bubble, so only bubbled chans pay the cost of tracking. (Do we have an existing weak map implementation?)

              Line 901, Patchset 24: // We don't want the goroutine's synctest group to become idle,
              Michael Pratt . resolved

              Why would this happen? Won't we ready the goroutine before our's parks? I assume I'm missing something here.

              Damien Neil

              Good point. I believe this predates my hooking into casgstatus for bookkeeping, so the accounting of the goroutine waking occurred later. It also predates the association of channels with bubbles, in which an unbubbled goroutine could wake a bubbled one.

              Under the current design, I don't think we need to do anything tricky here--either we're waking a goroutine in the same bubble (and the current goroutine keeps the bubble awake until it parks), or we're operating on an unbubbled channel (and the bubbled goroutine is not "durably blocked".

              Dropped the incActive/decActive, added a cross-bubble-operation check.

              File src/runtime/coro.go
              Line 240, Patchset 24: sg.decActive()
              Michael Pratt . resolved

              Does this mean that a coroutine that is not executing because it is waiting on the next iteration is considered idle by synctest?

              Damien Neil

              Yes.

              File src/runtime/malloc.go
              Line 995, Patchset 24: // but avoids any contamination between GC assist and synctest.
              Michael Pratt . resolved

              Is this just about assists? If so, IMO it makes more sense in gcAssistAlloc.

              e.g., newUserArenaChunk (for GOEXPERIMENT=arena) can also assist but is missing this change.

              Damien Neil

              Moved this to gcAssistAlloc and gcStart.

              File src/runtime/mgc.go
              Line 621, Patchset 24: // Mark the group as active even if we block somewhere below.
              Michael Pratt . resolved

              gcBgMarkStartWorkers starts new goroutines. What prevents them from starting in the bubble? (Edit: isSystemGoroutine in newproc)

              It might be better to drop the syncGroup during this function like mallocgc does.

              Damien Neil

              Done.

              File src/runtime/mgcmark.go
              Line 426, Patchset 24: // Mark the group as active even if we block somewhere below.
              Michael Pratt . resolved

              Why is this necessary given mallocgc drops the syncGroup?

              Damien Neil

              Changed mallocgc to not drop the syncGroup, dropping the syncGroup here to cover both mallocgc and anything else that calls gcAssistAlloc.

              File src/runtime/proc.go
              Line 1462, Patchset 24: gp.syncGroup.incActive()
              Michael Pratt . resolved

              Why?

              Damien Neil

              Because semacquire(&worldsema) can block on something external to the bubble, and we don't want to mark the bubble as durably blocked if it does.

              But the simpler way to do this is to consider waitReasonSemacquire as a non-durable blocking status; changed to do so and dropped the incActive here. This requires giving sync.WaitGroup.Wait its own identifiable waitReason, so I added waitReasonSyncWaitGroupWait.

              File src/runtime/select.go
              Line 168, Patchset 24: nonsynctest := false
              Michael Pratt . resolved

              nit: multiple negatives are a little confusing. Perhaps invert this and call it allSynctest.

              Damien Neil

              Done.

              File src/runtime/sema.go
              Line 623, Patchset 24: throw("semaphore wake of synctest goroutine from outside bubble")
              Michael Pratt . resolved

              Print the id of both synctest groups? You'll thank me later.

              Damien Neil

              Done. (Printing just the id of the goroutine being woken, which I think is enough.)

              File src/runtime/sizeof_test.go
              Line 23, Patchset 24: {runtime.G{}, 276, 448}, // g, but exported for testing
              Michael Pratt . resolved

              Why 16 bytes?

              Damien Neil

              Padding, moved the field adjacent to another pointer to avoid.

              File src/runtime/synctest.go
              Line 17, Patchset 24: waiter *g // caller of synctest.Wait
              Michael Pratt . resolved

              What if two goroutines call Wait?

              (That seems fine semantically (though maybe confusing?), they both wake up when everything else is idle.)

              Edit: I see below that this panics. The Wait documentation should say so.

              Damien Neil

              Documented.

              I don't think there's any use case for double-waiting, so it's simplest to disallow it. If we did want to support it, I think we'd wake one goroutine at a time to maintain the guarantee that after Wait returns no other goroutines in the bubble are executing.

              Line 21, Patchset 24: active int // group will not idle when active > 0
              Michael Pratt . resolved

              The group will not idle when active > 0 or running > 0.

              Perhaps expand upon this in the comments. I was initially under the impression that active covered all reasons for the group to be non-idle, but it turns out it only covers reasons other than running goroutines. It would be nice to have a breakdown of what both of these fields cover.

              Damien Neil

              Done.

              Line 54, Patchset 24: racereleasemergeg(gp, sg.raceaddr())
              Michael Pratt . resolved

              Are the race semantics documented in a comment somewhere?

              Damien Neil

              There's a comment in synctestWait; added one to raceaddr as well for better discoverability.

              Michael Pratt . resolved

              Why is this necessary given that the sg.waiter/sg.root will be runnable soon (L89)? Is it just because we can't call goready with sg.mu held?

              If so, perhaps say so explicitly in the comment. My read of changegstatus above is that runnable goroutines do count towards running.

              Damien Neil

              Done.

              Line 106, Patchset 24: // active > 0 while root runs, so we know it isn't running now.
              Michael Pratt . resolved

              Not true. Root only increments active on L144, _after_ creating a goroutine for f (L140). That goroutine could exit/block before root ever increments active.

              But the root does have running > 0 at that point, so it should be OK.

              Damien Neil

              Done.

              Michael Pratt . resolved

              What is the advantage to incrementing active vs marking waitReasonSynctestRun as non-idle?

              Damien Neil

              When Run is in waitReasonSynctestRun, it's waiting for the group to become idle so that it can advance the fake clock, panic with a deadlock report, or end the test. In this state, we don't want to consider it non-idle.

              Line 228, Patchset 24: if gp.syncGroup.running == 0 && gp.syncGroup.active == 0 {
              Michael Pratt . resolved

              IIUC, in this case, maybeWakeLocked will have woken root, as maybeWakeLocked looks at sg.waiter, not sg.waiting.

              This seems like the wrong semantics, as the waiter and root will run at the same time.

              Should maybeWakeLocked be looking at sg.waiting, and if sg.waiter is nil just do nothing with the knowledge that this function will shortly run the waiter?

              Damien Neil

              I realize now that this can never happen, since gopark increments active while running the unlock function (synctestwait_c). If Wait is called while all other goroutines in the bubble are blocked, it will always park and then be woken when gopark decrements active.

              Changed to throw if the invariant changes.

              maybeWakeLocked can't wake the root while Wait is in progress, because either running > 0 (the waiting goroutine hasn't started to park yet), active > 0 (we're in gopark), or waiter != nil (we've made it through to the end of gopark).

              File src/runtime/time.go
              Line 17, Patchset 24:func timeNow() (sec int64, nsec int32, mono int64) {
              Michael Pratt . resolved

              Since these are pushed, I think typical naming would be time.now (in time) and time_now (in runtime).

              Same below (time.nano, time_nano).

              Damien Neil

              Changed naming of the runtime package functions, but I'm keeping the time package naming: time.now exists and always returns the real clock, time.runtimeNow/runtimeNano are new functions that return the possibly-fake clock.

              Line 21, Patchset 24: return sec, nsec, 0
              Michael Pratt . resolved

              I wonder if any programs will be broken by the lack of monotonic time. I suppose not since the fake time is monotonic anyway? Still, why not just return sg.now here?

              Damien Neil

              Done.

              Line 1091, Patchset 24: if gp.syncGroup != nil {
              Michael Pratt . resolved

              Do I understand correctly that we expect this to always be false because we expect to always be on a g0?

              (Channel timers can't get here in synctest mode)

              Damien Neil

              I think that's correct, but I'm not 100% certain I know what a g0 is.

              Michael Pratt . resolved

              Comment no need to do anything because root will do it?

              Damien Neil

              Done.

              File src/runtime/time_linux_amd64.s
              Line 14, Patchset 24:TEXT time·now<ABIInternal>(SB),NOSPLIT,$16-24
              Michael Pratt . resolved

              IMO, this should be renamed to something in runtime now that it is unused by the time package.

              Damien Neil

              Unfortunately, time.now is used via //go:linkname by various packages.

              File src/time/time.go
              Line 1303, Patchset 24:func now() (sec int64, nsec int32, mono int64)
              Michael Pratt . resolved

              This has no callers, remove?

              Damien Neil

              Unfortunately, used via //go:linkname by various packages.

              The "this is used via linkname" comment was in runtime/timestub.go for some reason; I've copied it here for clarity.

              Open in Gerrit

              Related details

              Attention is currently required from:
              • Gavin Li
              • Michael Knyszek
              • Michael Pratt
              Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 25
                Gerrit-Owner: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
                Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
                Gerrit-CC: Gavin Li <gfl...@gmail.com>
                Gerrit-Attention: Michael Knyszek <mkny...@google.com>
                Gerrit-Attention: Michael Pratt <mpr...@google.com>
                Gerrit-Attention: Gavin Li <gfl...@gmail.com>
                Gerrit-Comment-Date: Mon, 18 Nov 2024 22:30:36 +0000
                Gerrit-HasComments: Yes
                Gerrit-Has-Labels: No
                Comment-In-Reply-To: Michael Pratt <mpr...@google.com>
                unsatisfied_requirement
                satisfied_requirement
                open
                diffy

                Michael Pratt (Gerrit)

                unread,
                Nov 19, 2024, 10:54:45 AM11/19/24
                to Damien Neil, goph...@pubsubhelper.golang.org, Michael Pratt, Go LUCI, Michael Knyszek, Gavin Li, golang-co...@googlegroups.com
                Attention needed from Damien Neil, Gavin Li and Michael Knyszek

                Michael Pratt voted and added 7 comments

                Votes added by Michael Pratt

                Code-Review+2

                7 comments

                File src/runtime/chan.go
                Line 38, Patchset 24: synctest bool // true if created in a synctest bubble
                Michael Pratt . resolved

                Can you remind me why we don't track the specific bubble? Is it just to save space?

                Damien Neil

                Just to save space. A bool here doesn't change the size of hchan. I don't want to increase the size of every production program's channels for a test-only feature.

                If we want to be more robust about catching cross-bubble operations, we could have a global map of chan->bubble, so only bubbled chans pay the cost of tracking. (Do we have an existing weak map implementation?)

                Michael Pratt

                Makes sense. We can always do more tracking in the future to be more strict. We have a weak package now, but no prebuilt weak-keyed map.

                Line 904, Patchset 25 (Latest): throw("channel wake of synctest goroutine " + string(itoa(gbuf[:], sgp.g.goid)) + " from outside bubble")
                Michael Pratt . resolved

                Huh, I've never thought of doing this for a dynamic throw argument. Usually we use a separate print:

                ```
                println("runtime: channel wake of synctest goroutine", sgp.g.goid, "from outside bubble")
                throw("channel wake of synctest goroutine from outside bubble")
                ```

                IMO, the print is cleaner, but I suppose itoa is OK as well.

                Line 904, Patchset 25 (Latest): throw("channel wake of synctest goroutine " + string(itoa(gbuf[:], sgp.g.goid)) + " from outside bubble")
                Michael Pratt . unresolved

                For these violations, perhaps these should use fatal instead of throw?

                https://cs.opensource.google/go/go/+/master:src/runtime/panic.go;l=1111

                The general idea is that fatal is for user error, while throw is an internal runtime problem.

                File src/runtime/mgc.go
                Line 645, Patchset 25 (Latest): // but avoids any contamination between GC assist and synctest.
                Michael Pratt . unresolved
                ```suggestion
                // but avoids any contamination between GC and synctest.
                ```
                File src/runtime/synctest.go
                Line 23, Patchset 25 (Latest): // Goroutines which are either running, or non-durably blocked
                Michael Pratt . resolved
                ```suggestion
                // Goroutines which are either running, runnable, or non-durably blocked
                ```

                nit: say runnable here? I suppose you could say that runnable goroutines are non-durably "blocked" by the scheduler, but that doesn't come to my mind immediately, so being explicit is nice.

                File src/runtime/time.go
                Line 1091, Patchset 24: if gp.syncGroup != nil {
                Michael Pratt . resolved

                Do I understand correctly that we expect this to always be false because we expect to always be on a g0?

                (Channel timers can't get here in synctest mode)

                Damien Neil

                I think that's correct, but I'm not 100% certain I know what a g0 is.

                Michael Pratt

                "g0" is runtime.m.g0. Each M has a g struct named g0 that holds a stack that the M can use. Notably, this is used for systemstack(). g0 never runs user code.

                File src/runtime/time_linux_amd64.s
                Line 14, Patchset 24:TEXT time·now<ABIInternal>(SB),NOSPLIT,$16-24
                Michael Pratt . resolved

                IMO, this should be renamed to something in runtime now that it is unused by the time package.

                Damien Neil

                Unfortunately, time.now is used via //go:linkname by various packages.

                Michael Pratt
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Damien Neil
                • Gavin Li
                • Michael Knyszek
                Submit Requirements:
                • requirement satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 25
                Gerrit-Owner: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
                Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
                Gerrit-CC: Gavin Li <gfl...@gmail.com>
                Gerrit-Attention: Michael Knyszek <mkny...@google.com>
                Gerrit-Attention: Damien Neil <dn...@google.com>
                Gerrit-Attention: Gavin Li <gfl...@gmail.com>
                Gerrit-Comment-Date: Tue, 19 Nov 2024 15:54:41 +0000
                Gerrit-HasComments: Yes
                Gerrit-Has-Labels: Yes
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                Damien Neil (Gerrit)

                unread,
                Nov 19, 2024, 12:31:33 PM11/19/24
                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                Attention needed from Damien Neil, Gavin Li and Michael Knyszek

                Damien Neil uploaded new patchset

                Damien Neil uploaded patch set #26 to this change.
                Following approvals got outdated and were removed:
                • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Damien Neil
                • Gavin Li
                • Michael Knyszek
                Submit Requirements:
                • requirement satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 26
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                Damien Neil (Gerrit)

                unread,
                Nov 19, 2024, 1:13:06 PM11/19/24
                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                Attention needed from Damien Neil, Gavin Li and Michael Knyszek

                Damien Neil uploaded new patchset

                Damien Neil uploaded patch set #27 to this change.
                Following approvals got outdated and were removed:
                • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Damien Neil
                • Gavin Li
                • Michael Knyszek
                Submit Requirements:
                • requirement satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 27
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                Damien Neil (Gerrit)

                unread,
                Nov 19, 2024, 1:15:00 PM11/19/24
                to goph...@pubsubhelper.golang.org, Go LUCI, Michael Pratt, Michael Knyszek, Gavin Li, golang-co...@googlegroups.com
                Attention needed from Gavin Li and Michael Knyszek

                Damien Neil added 4 comments

                File src/runtime/chan.go
                Line 904, Patchset 25: throw("channel wake of synctest goroutine " + string(itoa(gbuf[:], sgp.g.goid)) + " from outside bubble")
                Michael Pratt . resolved

                For these violations, perhaps these should use fatal instead of throw?

                https://cs.opensource.google/go/go/+/master:src/runtime/panic.go;l=1111

                The general idea is that fatal is for user error, while throw is an internal runtime problem.

                Damien Neil

                Good point. I was using fatal here because panic won't properly unwind the held locks, but this should be a panic.

                Dropping the throw here, added a panic in send/recv to catch cross-bubble operations.

                File src/runtime/mgc.go
                Line 645, Patchset 25: // but avoids any contamination between GC assist and synctest.
                Michael Pratt . resolved
                ```suggestion
                // but avoids any contamination between GC and synctest.
                ```
                Damien Neil

                Done

                File src/runtime/mklockrank.go
                Line 99, Patchset 27 (Latest):hchan, root, timers, timer, notifyList, reflectOffs < synctest;
                Damien Neil . resolved

                Small change to lock ranking to add `reflectOffs < synctest`.

                File src/runtime/synctest.go
                Line 23, Patchset 25: // Goroutines which are either running, or non-durably blocked
                Michael Pratt . resolved
                ```suggestion
                // Goroutines which are either running, runnable, or non-durably blocked
                ```

                nit: say runnable here? I suppose you could say that runnable goroutines are non-durably "blocked" by the scheduler, but that doesn't come to my mind immediately, so being explicit is nice.

                Damien Neil

                Done.

                Open in Gerrit

                Related details

                Attention is currently required from:
                • Gavin Li
                • Michael Knyszek
                Submit Requirements:
                • requirement satisfiedCode-Review
                • requirement satisfiedNo-Unresolved-Comments
                • requirement satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 27
                Gerrit-Owner: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
                Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
                Gerrit-CC: Gavin Li <gfl...@gmail.com>
                Gerrit-Attention: Michael Knyszek <mkny...@google.com>
                Gerrit-Attention: Gavin Li <gfl...@gmail.com>
                Gerrit-Comment-Date: Tue, 19 Nov 2024 18:14:52 +0000
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                Damien Neil (Gerrit)

                unread,
                Nov 19, 2024, 1:44:24 PM11/19/24
                to goph...@pubsubhelper.golang.org, Go LUCI, Michael Pratt, Michael Knyszek, Gavin Li, golang-co...@googlegroups.com
                Attention needed from Gavin Li and Michael Knyszek

                Damien Neil voted Commit-Queue+1

                Commit-Queue+1
                Gerrit-Comment-Date: Tue, 19 Nov 2024 18:44:17 +0000
                Gerrit-HasComments: No
                Gerrit-Has-Labels: Yes
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                Damien Neil (Gerrit)

                unread,
                Nov 19, 2024, 1:53:15 PM11/19/24
                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                Attention needed from Damien Neil, Gavin Li and Michael Knyszek

                Damien Neil uploaded new patchset

                Damien Neil uploaded patch set #28 to this change.
                Following approvals got outdated and were removed:
                • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Damien Neil
                • Gavin Li
                • Michael Knyszek
                Submit Requirements:
                • requirement satisfiedCode-Review
                • requirement satisfiedNo-Unresolved-Comments
                • requirement satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 28
                Gerrit-Owner: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
                Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
                Gerrit-CC: Gavin Li <gfl...@gmail.com>
                Gerrit-Attention: Michael Knyszek <mkny...@google.com>
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                Damien Neil (Gerrit)

                unread,
                Nov 19, 2024, 2:40:47 PM11/19/24
                to goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Go LUCI, Michael Pratt, Michael Knyszek, Gavin Li, golang-co...@googlegroups.com

                Damien Neil submitted the change with unreviewed changes

                Unreviewed changes

                25 is the latest approved patch-set.
                The change was submitted with unreviewed changes in the following files:

                ```
                The name of the file: src/runtime/chan.go
                Insertions: 8, Deletions: 7.

                @@ -316,6 +316,10 @@
                // sg must already be dequeued from c.
                // ep must be non-nil and point to the heap or the caller's stack.
                func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
                + if c.synctest && sg.g.syncGroup != getg().syncGroup {
                + unlockf()
                + panic(plainError("send on synctest channel from outside bubble"))
                + }
                if raceenabled {
                if c.dataqsiz == 0 {
                racesync(c, sg)
                @@ -693,6 +697,10 @@
                // sg must already be dequeued from c.
                // A non-nil ep must point to the heap or the caller's stack.
                func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
                + if c.synctest && sg.g.syncGroup != getg().syncGroup {
                + unlockf()
                + panic(plainError("receive on synctest channel from outside bubble"))
                + }
                if c.dataqsiz == 0 {
                if raceenabled {
                racesync(c, sg)
                @@ -897,13 +905,6 @@
                // else has won the race to signal this goroutine but the goroutine
                // hasn't removed itself from the queue yet.
                if sgp.isSelect {
                - if sg := sgp.g.syncGroup; sg != nil {
                - gp := getg()
                - if gp.syncGroup != nil && gp.syncGroup != sg {
                - var gbuf [20]byte
                - throw("channel wake of synctest goroutine " + string(itoa(gbuf[:], sgp.g.goid)) + " from outside bubble")
                - }
                - }
                if !sgp.g.selectDone.CompareAndSwap(0, 1) {
                // We lost the race to wake this goroutine.
                continue
                ```
                ```
                The name of the file: src/runtime/lockrank.go
                Insertions: 13, Deletions: 13.

                @@ -41,9 +41,9 @@
                lockRankTimer
                lockRankNetpollInit
                lockRankRoot
                - lockRankSynctest
                lockRankItab
                lockRankReflectOffs
                + lockRankSynctest
                lockRankUserArenaState
                // TRACEGLOBAL
                lockRankTraceBuf
                @@ -115,9 +115,9 @@
                lockRankTimer: "timer",
                lockRankNetpollInit: "netpollInit",
                lockRankRoot: "root",
                - lockRankSynctest: "synctest",
                lockRankItab: "itab",
                lockRankReflectOffs: "reflectOffs",
                + lockRankSynctest: "synctest",
                lockRankUserArenaState: "userArenaState",
                lockRankTraceBuf: "traceBuf",
                lockRankTraceStrings: "traceStrings",
                @@ -196,9 +196,9 @@
                lockRankTimer: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankTimers},
                lockRankNetpollInit: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankTimers, lockRankTimer},
                lockRankRoot: {},
                - lockRankSynctest: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankRoot},
                lockRankItab: {},
                lockRankReflectOffs: {lockRankItab},
                + lockRankSynctest: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankRoot, lockRankItab, lockRankReflectOffs},
                lockRankUserArenaState: {},
                lockRankTraceBuf: {lockRankSysmon, lockRankScavenge},
                lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf},
                @@ -211,16 +211,16 @@
                lockRankProfBlock: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
                lockRankProfMemActive: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
                lockRankProfMemFuture: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankProfMemActive},
                - lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture},
                - lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
                - lockRankStackLarge: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
                - lockRankHchanLeaf: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankHchanLeaf},
                - lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
                - lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans},
                - lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
                - lockRankGlobalAlloc: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankMheapSpecial},
                - lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
                - lockRankTraceStackTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankSynctest, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankTrace},
                + lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture},
                + lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
                + lockRankStackLarge: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
                + lockRankHchanLeaf: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankHchanLeaf},
                + lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
                + lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans},
                + lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
                + lockRankGlobalAlloc: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankMheapSpecial},
                + lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
                + lockRankTraceStackTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankSynctest, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankTrace},
                lockRankPanic: {},
                lockRankDeadlock: {lockRankPanic, lockRankDeadlock},
                lockRankRaceFini: {lockRankPanic},
                ```
                ```
                The name of the file: src/runtime/synctest.go
                Insertions: 1, Deletions: 1.

                @@ -20,7 +20,7 @@
                // The group is active (not blocked) so long as running > 0 || active > 0.
                //
                // running is the number of goroutines which are not "durably blocked":
                - // Goroutines which are either running, or non-durably blocked
                + // Goroutines which are either running, runnable, or non-durably blocked
                // (for example, blocked in a syscall).
                //
                // active is used to keep the group from becoming blocked,
                ```
                ```
                The name of the file: src/runtime/mklockrank.go
                Insertions: 3, Deletions: 3.

                @@ -90,14 +90,14 @@
                # Semaphores
                NONE < root;

                -# Synctest
                -hchan, root, timers, timer, notifyList < synctest;
                -
                # Itabs
                NONE
                < itab
                < reflectOffs;

                +# Synctest
                +hchan, root, timers, timer, notifyList, reflectOffs < synctest;
                +
                # User arena state
                NONE < userArenaState;

                ```
                ```
                The name of the file: src/runtime/mgc.go
                Insertions: 1, Deletions: 1.

                @@ -642,7 +642,7 @@
                if gp := getg(); gp.syncGroup != nil {
                // Disassociate the G from its synctest bubble while allocating.
                // This is less elegant than incrementing the group's active count,
                - // but avoids any contamination between GC assist and synctest.
                + // but avoids any contamination between GC and synctest.
                sg := gp.syncGroup
                gp.syncGroup = nil
                defer func() {
                ```
                ```
                The name of the file: src/runtime/sema.go
                Insertions: 4, Deletions: 4.

                @@ -630,8 +630,8 @@
                next := s.next
                s.next = nil
                if s.g.syncGroup != nil && getg().syncGroup != s.g.syncGroup {
                - var gbuf [20]byte
                - throw("semaphore wake of synctest goroutine " + string(itoa(gbuf[:], s.g.goid)) + " from outside bubble")
                + println("semaphore wake of synctest goroutine", s.g.goid, "from outside bubble")
                + panic("semaphore wake of synctest goroutine from outside bubble")
                }
                readyWithTime(s, 4)
                s = next
                @@ -687,8 +687,8 @@
                unlock(&l.lock)
                s.next = nil
                if s.g.syncGroup != nil && getg().syncGroup != s.g.syncGroup {
                - var gbuf [20]byte
                - throw("semaphore wake of synctest goroutine " + string(itoa(gbuf[:], s.g.goid)) + " from outside bubble")
                + println("semaphore wake of synctest goroutine", s.g.goid, "from outside bubble")
                + panic("semaphore wake of synctest goroutine from outside bubble")
                }
                readyWithTime(s, 4)
                return
                ```

                Change information

                Commit message:
                internal/synctest: new package for testing concurrent code

                Add an internal (for now) implementation of testing/synctest.

                The synctest.Run function executes a tree of goroutines in an
                isolated environment using a fake clock. The synctest.Wait function
                allows a test to wait for all other goroutines within the test
                to reach a blocking point.

                For #67434
                For #69687
                Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Reviewed-by: Michael Pratt <mpr...@google.com>
                Files:
                • M src/go/build/deps_test.go
                • A src/internal/synctest/synctest.go
                • A src/internal/synctest/synctest_test.go
                • M src/runtime/chan.go
                • M src/runtime/coro.go
                • M src/runtime/lockrank.go
                • M src/runtime/mgc.go
                • M src/runtime/mgcmark.go
                • M src/runtime/mklockrank.go
                • M src/runtime/proc.go
                • M src/runtime/runtime2.go
                • M src/runtime/select.go
                • M src/runtime/sema.go
                • M src/runtime/sizeof_test.go
                • A src/runtime/synctest.go
                • A src/runtime/synctest_test.go
                • A src/runtime/testdata/testsynctest/main.go
                • M src/runtime/time.go
                • M src/runtime/time_linux_amd64.s
                • M src/runtime/traceback.go
                • M src/runtime/traceruntime.go
                • M src/sync/runtime.go
                • M src/sync/waitgroup.go
                • M src/time/time.go
                • M src/time/zoneinfo_plan9.go
                • M src/time/zoneinfo_read.go
                Change size: XL
                Delta: 26 files changed, 1155 insertions(+), 36 deletions(-)
                Branch: refs/heads/master
                Submit Requirements:
                • requirement satisfiedCode-Review: +2 by Michael Pratt
                • requirement satisfiedTryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
                Open in Gerrit
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: merged
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 29
                open
                diffy
                satisfied_requirement

                Dmitri Shuralyov (Gerrit)

                unread,
                Nov 19, 2024, 4:49:07 PM11/19/24
                to Damien Neil, goph...@pubsubhelper.golang.org, Dmitri Shuralyov, Go LUCI, Michael Pratt, Gavin Li, golang-co...@googlegroups.com

                Dmitri Shuralyov added 1 comment

                Open in Gerrit

                Related details

                Attention set is empty
                Submit Requirements:
                • requirement satisfiedCode-Review
                • requirement satisfiedNo-Unresolved-Comments
                • requirement satisfiedReview-Enforcement
                • requirement satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc
                Gerrit-Change-Number: 591997
                Gerrit-PatchSet: 29
                Gerrit-Owner: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Damien Neil <dn...@google.com>
                Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
                Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
                Gerrit-CC: Dmitri Shuralyov <dmit...@golang.org>
                Gerrit-CC: Gavin Li <gfl...@gmail.com>
                Gerrit-Comment-Date: Tue, 19 Nov 2024 21:49:02 +0000
                Gerrit-HasComments: Yes
                Gerrit-Has-Labels: No
                satisfied_requirement
                open
                diffy
                Reply all
                Reply to author
                Forward
                0 new messages