Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
First attempt at implementing select.
Browse files Browse the repository at this point in the history
This contains a rewrite of the various waiters in q.go to support select
operations. It then leverages this new waiter design to implement read
and write select for unbounded and bounded channels.

Currently the only code that uses Select is test code -- that will change when I
am more confident in the API and have written more documentation.

Notes:
  - Both kinds of waiter (waiter and weakwaiter) have been replaced by a
    condWaiter. condwaiters act like waiters except there can be an arbitrary
    number of sends, but only one send will succeed. Furthermore, senders are
    notified if their send was successful. Send operations may also be cancelled
    by a receiver, in which case the cancelled send will also report failure.

    Conwaiters were introduced to facilitate select operations. As such
    not only do they allow for a value to be stored, but they also keep
    track of which channel has successfully sent on them. condWaiter
    send/recv methods have "overlapping" variants in which sends will
    report success if any sender from the same channel succeeded. This
    is used to accommodate the subtlety of multiple receive threads
    attempting to wake up a blocking sender (an issue present in the
    initial design).

    All standard channel protocols are essentially unchanged,
    except that if their send operation fails they retry the entire
    send/receive until they are successful.

  - Given this new object, a Select operates on a list of SelectRead or
    SelectWrite requests. The operation creates a new condWaiter and performs
    the first part of the read and write operations for the channel, these parts
    are:

      - For reading: Increment the H index and either return the value in the
        relevant location or CAS in the waiter object.
      - For writing: Increment the T index and either perform the send protocol
        (always true for the unbounded case) or potentially block by placing the
        condWaiter in its relevant location.

    If any of these processes succeed (i.e. no blocking on the
    condWaiter is required) then the condWaiter is cancelled and that
    value is returned. If blocking is required for all read/write
    requests, the thread then blocks on a waiter and is awoken by the
    sender/receiver who will win the select.

  - One additional subtlety here is that in the bounded case, blocking
    waiters are still woken up _even if the cell chanSize ahead of them
    represents a failed select_. While this is counter-intuitive (and
    requires justification!), it seems to preserve the intended semantics.

  - All of this required restructuring the enqueue/dequeue code so that it
    executes an initial non-blocking step and then passes a callback for the
    remaining portion of the protocol to be executed if the send on the
    condWaiter succeeds. This has resulted in code that is less readable than it
    was before -- more cleanup is warranted.

  - I was recently informed that a select on N operations, k of which do
    not require blocking, must choose which channel to succeed randomly
    among those k. I currently do not do this, but implementing it should
    be as simple as iterating over the request in a random order.

Caveats:
  - This implementation contains a lot of subtle moves, so it
    probably has bugs.
  - There are currently some performance regressions (particularly for
    buffered channels) compared to send/recv performance before select
    support was implemented. These need to be addressed. I see no reason
    why this code should be much slower, but I would like to come up
    with a way that doesn't have a ton of special cases related to
    detecting if a waiting thread is selecting or not.
  - These channels still do not support close, so they are not at feature parity
    with go channels yet. However, I suspect that select is the more difficult
    of the two to implement.
  • Loading branch information
ezrosent committed Jan 30, 2017
1 parent 9cd8047 commit 81099c5
Show file tree
Hide file tree
Showing 5 changed files with 668 additions and 265 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.swp
.DS_Store
src/bench/bench

0 comments on commit 81099c5

Please sign in to comment.