summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/ccommon_api.md12
-rw-r--r--include/stc/algo/coroutine.h79
-rw-r--r--misc/examples/dining_philosophers.c12
3 files changed, 64 insertions, 39 deletions
diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md
index 4d18120a..6276494b 100644
--- a/docs/ccommon_api.md
+++ b/docs/ccommon_api.md
@@ -379,25 +379,27 @@ To resume the coroutine from where it was suspended with *cco_yield()*, simply c
| | `cco_yield(ret)` | Yield/suspend execution and return ret |
| | `cco_await(promise)` | Await/suspend until promise is true |
| | `cco_await(promise, ret)` | Await/suspend with ret value |
-| | `cco_sleep(long msec)` | Sleep for milliseconds |
| | Semaphores: | |
| | `cco_sem` | Semaphore type |
| | `cco_sem_await(sem)` | Await for the semaphore count > 0 |
| | `cco_sem_await(sem, ret)` | Await with ret on the semaphore |
-| cco_sem | `cco_sem_init(long value)` | Set semaphore value |
+| `cco_sem` | `cco_sem_init(long value)` | Set semaphore value |
| | `cco_sem_release(sem)` | Signal the semaphore |
| | Timers: | |
| | `cco_timer` | Timer type |
| | `cco_timer_await(tm)` | Await for timer to expire |
| | `cco_timer_await(tm, ret)` | Await with ret for timer to expire |
-| | `cco_timer_start(tm, long msecs)` | Start timer msecs milliseconds |
+| | `cco_timer_start(tm, long usec)` | Start timer for usec microseconds |
| | `cco_timer_restart(tm)` | Restart timer with same duration |
| `bool` | `cco_timer_expired(tm)` | Return true if timer is expired |
-| `long` | `cco_timer_remaining(tm)` | Return milliseconds remaining |
+|`long long`| `cco_timer_remaining(tm)` | Return microseconds remaining |
| | From caller side: | |
-| `void` | `cco_stop(co)` | Next call of coroutine returns `cco_end()` |
+| `void` | `cco_stop(co)` | Next call of coroutine finalizes |
| `void` | `cco_reset(co)` | Reset state to initial (for reuse) |
| `void` | `cco_run(co, corocall) { }` | Run blocking until coro is done |
+| | Time functions: | |
+|`long long`| `cco_utime(void)` | Return microseconds since Epoch |
+| | `cco_usleep(long long usec)` | Sleep for microseconds |
---
## RAII scope macros
diff --git a/include/stc/algo/coroutine.h b/include/stc/algo/coroutine.h
index 05307b08..d0c9ad86 100644
--- a/include/stc/algo/coroutine.h
+++ b/include/stc/algo/coroutine.h
@@ -56,7 +56,6 @@ int main(void) {
return 0;
}
*/
-#include <time.h>
#include <stc/ccommon.h>
enum {
@@ -130,55 +129,77 @@ typedef struct {
* Timer
*/
-typedef struct {
- clock_t start;
- clock_t interval;
-} cco_timer;
-
-#define cco_timer_await(...) c_MACRO_OVERLOAD(cco_timer_await, __VA_ARGS__)
-#define cco_timer_await_2(tm, msec) cco_timer_await_3(tm, msec, )
-#define cco_timer_await_3(tm, msec, ret) \
- do { \
- cco_timer_start(tm, msec); \
- cco_await_2(cco_timer_expired(tm), ret); \
- } while (0)
-
#ifdef _WIN32
#ifdef __cplusplus
- extern "C"
+ #define _c_LINKC extern "C" __declspec(dllimport)
+ #else
+ #define _c_LINKC __declspec(dllimport)
#endif
- __declspec(dllimport) void __stdcall Sleep(unsigned long);
- static inline void cco_sleep(long msec) {
- Sleep((unsigned long)msec);
+ struct _FILETIME; struct _SECURITY_ATTRIBUTES; union _LARGE_INTEGER;
+ _c_LINKC void GetSystemTimePreciseAsFileTime(struct _FILETIME*);
+ _c_LINKC void* CreateWaitableTimerW(struct _SECURITY_ATTRIBUTES*, int, const wchar_t*);
+ _c_LINKC int SetWaitableTimer(void*, const union _LARGE_INTEGER*, long, void(*)(void*, unsigned long, unsigned long), void*, int);
+ _c_LINKC unsigned long WaitForSingleObject(void*, unsigned long);
+ _c_LINKC int CloseHandle(void*);
+
+ static inline long long cco_utime(void) {
+ static const long long epoch_offset = 11644473600000000LL; /* microseconds betweeen Jan 1,1601 - Jan 1,1970 */
+ unsigned long long quad; /* 64-bit value, 100-nanosecond intervals since January 1, 1601 00:00 UTC */
+ GetSystemTimePreciseAsFileTime((struct _FILETIME*)&quad);
+ return (long long)quad/10 - epoch_offset; /* microseconds since epoch */
}
+
+ static inline void cco_usleep(long long usec) {
+ unsigned long long ft = -10*usec;
+ void* timer = CreateWaitableTimerW(NULL, true, NULL);
+ SetWaitableTimer(timer, (const union _LARGE_INTEGER*)&ft, 0, NULL, NULL, 0);
+ WaitForSingleObject(timer, ~0UL);
+ CloseHandle(timer);
+ }
#else
#include <sys/time.h>
- static inline void cco_sleep(long msec) {
- struct timeval tv = {.tv_sec=msec/1000, .tv_usec=1000*(msec % 1000)};
+ static inline long long cco_utime(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec*1000000LL + tv.tv_usec;
+ }
+
+ static inline void cco_usleep(long long usec) {
+ struct timeval tv = {.tv_sec=(time_t)(usec/1000000), .tv_usec=(suseconds_t)(usec % 1000000)};
select(0, NULL, NULL, NULL, &tv);
}
#endif
-static inline void cco_timer_start(cco_timer* tm, long msec) {
- tm->interval = msec*(CLOCKS_PER_SEC/1000);
- tm->start = clock();
+typedef struct { long long interval, start; } cco_timer;
+
+#define cco_timer_await(...) c_MACRO_OVERLOAD(cco_timer_await, __VA_ARGS__)
+#define cco_timer_await_2(tm, usec) cco_timer_await_3(tm, usec, )
+#define cco_timer_await_3(tm, usec, ret) \
+ do { \
+ cco_timer_start(tm, usec); \
+ cco_await_2(cco_timer_expired(tm), ret); \
+ } while (0)
+
+static inline void cco_timer_start(cco_timer* tm, long long usec) {
+ tm->interval = usec;
+ tm->start = cco_utime();
}
-static inline cco_timer cco_timer_from(long msec) {
- cco_timer tm = {msec*(CLOCKS_PER_SEC/1000), clock()};
+static inline cco_timer cco_timer_from(long long usec) {
+ cco_timer tm = {.interval=usec, .start=cco_utime()};
return tm;
}
static inline void cco_timer_restart(cco_timer* tm) {
- tm->start = clock();
+ tm->start = cco_utime();
}
static inline bool cco_timer_expired(cco_timer* tm) {
- return clock() - tm->start >= tm->interval;
+ return cco_utime() - tm->start >= tm->interval;
}
-static inline long cco_timer_remaining(cco_timer* tm) {
- return (long)((double)(tm->start + tm->interval - clock())*(1000.0/CLOCKS_PER_SEC));
+static inline long long cco_timer_remaining(cco_timer* tm) {
+ return tm->start + tm->interval - cco_utime();
}
#endif
diff --git a/misc/examples/dining_philosophers.c b/misc/examples/dining_philosophers.c
index 0bf421c6..cc6e5fd2 100644
--- a/misc/examples/dining_philosophers.c
+++ b/misc/examples/dining_philosophers.c
@@ -1,5 +1,6 @@
// https://en.wikipedia.org/wiki/Dining_philosophers_problem
#include <stdio.h>
+#include <time.h>
#include <stc/crand.h>
#include <stc/algo/coroutine.h>
@@ -33,7 +34,7 @@ void philosopher(struct Philosopher* p)
while (1) {
int duration = (int)(1000 + crand() % 2000); // 1-3 seconds
printf("Philosopher %d is thinking for %d minutes...\n", p->id, duration/100);
- cco_timer_await(&p->tm, duration);
+ cco_timer_await(&p->tm, duration*1000);
printf("Philosopher %d is hungry...\n", p->id);
cco_sem_await(p->left_fork);
@@ -41,7 +42,7 @@ void philosopher(struct Philosopher* p)
duration = (int)(500 + crand() % 1000);
printf("Philosopher %d is eating for %d minutes...\n", p->id, duration/100);
- cco_timer_await(&p->tm, duration);
+ cco_timer_await(&p->tm, duration*1000);
cco_sem_release(p->left_fork);
cco_sem_release(p->right_fork);
@@ -67,10 +68,11 @@ void dining(struct Dining* d)
}
while (1) {
+ // per-"frame" logic update of all philosophers states
for (d->ph_idx = 0; d->ph_idx < num_philosophers; ++d->ph_idx) {
philosopher(&d->ph[d->ph_idx]);
- cco_yield();
}
+ cco_yield(); // suspend, return control back to main
}
cco_final:
@@ -87,13 +89,13 @@ int main()
{
struct Dining dine;
cco_reset(&dine);
- cco_timer tm = cco_timer_from(10000);
+ cco_timer tm = cco_timer_from(10*1000000); // microseconds
csrand((uint64_t)time(NULL));
while (!cco_done(&dine)) {
if (cco_timer_expired(&tm))
cco_stop(&dine);
dining(&dine);
- cco_sleep(1);
+ cco_usleep(100);
}
}