Skip to content

Commit f3c68c4

Browse files
committed
disposable_semaphore for one-shot usage
1 parent bb84afb commit f3c68c4

3 files changed

Lines changed: 68 additions & 0 deletions

File tree

thread/test/test.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ limitations under the License.
3232
#include "../../test/ci-tools.h"
3333
#include "../future.h"
3434

35+
#include <photon/common/lockfree_queue.h>
3536

3637
using namespace std;
3738
using namespace photon;
@@ -698,6 +699,40 @@ TEST(Semaphore, heavy) {
698699
wait_for_completion();
699700
}
700701

702+
using Ring = photon::common::RingChannel<
703+
LockfreeSPSCRingQueue<Delegate<void>, 16>>;
704+
705+
void test_disposable_semaphore(Ring* ring, uint64_t count, uint64_t slat, uint64_t oplat) {
706+
while (count--) {
707+
disposable_semaphore sem;
708+
auto op = [oplat, &sem]() {
709+
if (oplat) ::usleep(oplat); // operation latency
710+
sem.signal(); // completion signal
711+
};
712+
ring->send(op);
713+
if (slat) ::usleep(slat); // submission latency
714+
int ret = sem.wait(1000*1000); // wait for completion
715+
EXPECT_EQ(0, ret);
716+
if (ret) return;
717+
}
718+
}
719+
720+
TEST(disposable_semaphore, basic) {
721+
Ring ring;
722+
std::thread worker([&]() {
723+
vcpu_init();
724+
while(auto cb = ring.recv(0, 0)) cb();
725+
vcpu_fini();
726+
});
727+
728+
test_disposable_semaphore(&ring, 10000, 0, 100);
729+
test_disposable_semaphore(&ring, 10000, 100, 0);
730+
test_disposable_semaphore(&ring, 10000, 00, 00);
731+
732+
ring.send({});
733+
worker.join();
734+
}
735+
701736
TEST(Sleep, sleep_only_thread) { //Sleep_sleep_only_thread_Test::TestBody
702737
// If current thread is only thread and sleeping
703738
// it should be able to avoid crash during long time sleep

thread/thread.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,6 +1899,10 @@ R"(
18991899
}
19001900
return 0;
19011901
}
1902+
void disposable_semaphore::defer(void* arg) {
1903+
auto _this = (disposable_semaphore*)arg;
1904+
_this->signal();
1905+
}
19021906
bool is_master_event_engine_default() {
19031907
return CURRENT->get_vcpu()->is_master_event_engine_default();
19041908
}

thread/thread.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,35 @@ namespace photon
461461
void try_resume(uint64_t count);
462462
};
463463

464+
// one-shot semaphore
465+
class disposable_semaphore {
466+
public:
467+
int wait(Timeout timeout) {
468+
uint64_t th = 0, current = (uint64_t)CURRENT;
469+
assert(current && !(current & 1));
470+
if (!_waiter.compare_exchange_strong(th, current)) {
471+
assert(th == 0x1);
472+
return 0;
473+
}
474+
int ret = thread_usleep_defer(timeout, &defer, this);
475+
if (likely(ret < 0)) { assert(errno == -1); return 0; }
476+
errno = ETIMEDOUT;
477+
return -1;
478+
}
479+
void signal() {
480+
auto th = _waiter.fetch_or(0x1);
481+
if (!th) { } // first signal (async op completion) before the CAS, AKA _waiter.compare_exchange_strong()
482+
else if (!(th&1)) { } // first signal after the CAS, either async op completion or usleep defer
483+
else { assert((th>>1) && (th&1)); // second signal()
484+
thread_interrupt((thread*)(th&~1), -1);
485+
}
486+
}
487+
488+
protected:
489+
std::atomic<uint64_t> _waiter{0};
490+
static void defer(void* arg);
491+
};
492+
464493
// to be different to timer flags
465494
// mark flag should be larger than 999, and not touch lower bits
466495
// here we selected

0 commit comments

Comments
 (0)