@ -39,6 +39,7 @@
# include "src/core/lib/gprpp/sync.h"
# include "src/core/lib/iomgr/exec_ctx.h"
# include "src/core/lib/promise/activity.h"
# include "src/core/lib/promise/cancel_callback.h"
# include "src/core/lib/promise/if.h"
# include "src/core/lib/promise/map.h"
# include "src/core/lib/promise/poll.h"
@ -69,8 +70,10 @@ class PromiseEndpoint {
// `Write()` before the previous write finishes. Doing that results in
// undefined behavior.
auto Write ( SliceBuffer data ) {
// Assert previous write finishes.
GPR_ASSERT ( ! write_state_ - > complete . load ( std : : memory_order_relaxed ) ) ;
// Start write and assert previous write finishes.
auto prev = write_state_ - > state . exchange ( WriteState : : kWriting ,
std : : memory_order_relaxed ) ;
GPR_ASSERT ( prev = = WriteState : : kIdle ) ;
bool completed ;
if ( data . Length ( ) = = 0 ) {
completed = true ;
@ -92,16 +95,31 @@ class PromiseEndpoint {
if ( completed ) write_state_ - > waker = Waker ( ) ;
}
return If (
completed , [ ] ( ) { return [ ] ( ) { return absl : : OkStatus ( ) ; } ; } ,
completed ,
[ this ] ( ) {
return [ write_state = write_state_ ] ( ) {
auto prev = write_state - > state . exchange ( WriteState : : kIdle ,
std : : memory_order_relaxed ) ;
GPR_ASSERT ( prev = = WriteState : : kWriting ) ;
return absl : : OkStatus ( ) ;
} ;
} ,
[ this ] ( ) {
return [ write_state = write_state_ ] ( ) - > Poll < absl : : Status > {
// If current write isn't finished return `Pending()`, else return
// write result.
if ( ! write_state - > complete . load ( std : : memory_order_acquire ) ) {
return Pending ( ) ;
// If current write isn't finished return `Pending()`, else
// return write result.
WriteState : : State expected = WriteState : : kWritten ;
if ( write_state - > state . compare_exchange_strong (
expected , WriteState : : kIdle , std : : memory_order_acquire ,
std : : memory_order_relaxed ) ) {
// State was Written, and we changed it to Idle. We can return
// the result.
return std : : move ( write_state - > result ) ;
}
write_state - > complete . store ( false , std : : memory_order_relaxed ) ;
return std : : move ( write_state - > result ) ;
// State was not Written; since we're polling it must be
// Writing. Assert that and return Pending.
GPR_ASSERT ( expected = = WriteState : : kWriting ) ;
return Pending ( ) ;
} ;
} ) ;
}
@ -228,7 +246,13 @@ class PromiseEndpoint {
} ;
struct WriteState : public RefCounted < WriteState > {
std : : atomic < bool > complete { false } ;
enum State : uint8_t {
kIdle , // Not writing.
kWriting , // Write started, but not completed.
kWritten , // Write completed.
} ;
std : : atomic < State > state { kIdle } ;
// Write buffer used for `EventEngine::Endpoint::Write()` to ensure the
// memory behind the buffer is not lost.
grpc_event_engine : : experimental : : SliceBuffer buffer ;
@ -239,7 +263,10 @@ class PromiseEndpoint {
void Complete ( absl : : Status status ) {
result = std : : move ( status ) ;
auto w = std : : move ( waker ) ;
complete . store ( true , std : : memory_order_release ) ;
auto prev = state . exchange ( kWritten , std : : memory_order_release ) ;
// Previous state should be Writing. If we got anything else we've entered
// the callback path twice.
GPR_ASSERT ( prev = = kWriting ) ;
w . Wakeup ( ) ;
}
} ;