commit 5b94db2be33be124cbfbb9bdac123f2690f89768
parent 84c5daa19aa76209ed633581abc1f3c591ee1d8f
Author: finwo <finwo@pm.me>
Date: Wed, 18 Mar 2026 20:46:10 +0100
Add semaphores
Diffstat:
14 files changed, 719 insertions(+), 209 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -0,0 +1,334 @@
+---
+Language: Cpp
+AccessModifierOffset: -1
+AlignAfterOpenBracket: Align
+AlignArrayOfStructures: None
+AlignConsecutiveAssignments:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ AlignFunctionDeclarations: false
+ AlignFunctionPointers: false
+ PadOperators: true
+AlignConsecutiveBitFields:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ AlignFunctionDeclarations: false
+ AlignFunctionPointers: false
+ PadOperators: false
+AlignConsecutiveDeclarations:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ AlignFunctionDeclarations: true
+ AlignFunctionPointers: false
+ PadOperators: false
+AlignConsecutiveMacros:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ AlignFunctionDeclarations: false
+ AlignFunctionPointers: false
+ PadOperators: false
+AlignConsecutiveShortCaseStatements:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCaseArrows: false
+ AlignCaseColons: false
+AlignConsecutiveTableGenBreakingDAGArgColons:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ AlignFunctionDeclarations: false
+ AlignFunctionPointers: false
+ PadOperators: false
+AlignConsecutiveTableGenCondOperatorColons:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ AlignFunctionDeclarations: false
+ AlignFunctionPointers: false
+ PadOperators: false
+AlignConsecutiveTableGenDefinitionColons:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ AlignFunctionDeclarations: false
+ AlignFunctionPointers: false
+ PadOperators: false
+AlignEscapedNewlines: Left
+AlignOperands: Align
+AlignTrailingComments:
+ Kind: Always
+ OverEmptyLines: 0
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowBreakBeforeNoexceptSpecifier: Never
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseExpressionOnASingleLine: true
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortCompoundRequirementOnASingleLine: true
+AllowShortEnumsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: WithoutElse
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: true
+AllowShortNamespacesOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AttributeMacros:
+ - __capability
+ - absl_nonnull
+ - absl_nullable
+ - absl_nullability_unknown
+BinPackArguments: true
+BinPackLongBracedList: true
+BinPackParameters: BinPack
+BitFieldColonSpacing: Both
+BracedInitializerIndentWidth: -1
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: Never
+ AfterEnum: false
+ AfterExternBlock: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakAdjacentStringLiterals: true
+BreakAfterAttributes: Leave
+BreakAfterJavaFieldAnnotations: false
+BreakAfterReturnType: None
+BreakArrays: true
+BreakBeforeBinaryOperators: None
+BreakBeforeConceptDeclarations: Always
+BreakBeforeBraces: Attach
+BreakBeforeInlineASMColon: OnlyMultiline
+BreakBeforeTemplateCloser: false
+BreakBeforeTernaryOperators: true
+BreakBinaryOperations: Never
+BreakConstructorInitializers: BeforeColon
+BreakFunctionDefinitionParameters: false
+BreakInheritanceList: BeforeColon
+BreakStringLiterals: true
+BreakTemplateDeclarations: Yes
+ColumnLimit: 120
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+EnumTrailingComma: Leave
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^<ext/.*\.h>'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^<.*\.h>'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^<.*'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IncludeIsMainSourceRegex: ''
+IndentAccessModifiers: false
+IndentCaseBlocks: true
+IndentCaseLabels: true
+IndentExportBlock: true
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentRequiresClause: true
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+InsertBraces: false
+InsertNewlineAtEOF: true
+InsertTrailingCommas: None
+IntegerLiteralSeparator:
+ Binary: 0
+ BinaryMinDigits: 0
+ Decimal: 0
+ DecimalMinDigits: 0
+ Hex: 0
+ HexMinDigits: 0
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLines:
+ AtEndOfFile: false
+ AtStartOfBlock: false
+ AtStartOfFile: false
+KeepFormFeed: false
+LambdaBodyIndentation: Signature
+LineEnding: DeriveLF
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MainIncludeChar: Quote
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Never
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+OneLineFormatOffRegex: ''
+PackConstructorInitializers: NextLine
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakBeforeMemberAccess: 150
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakScopeResolution: 500
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 0
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Right
+PPIndentWidth: -1
+QualifierAlignment: Leave
+RawStringFormats:
+ - Language: Cpp
+ Delimiters:
+ - cc
+ - CC
+ - cpp
+ - Cpp
+ - CPP
+ - 'c++'
+ - 'C++'
+ CanonicalDelimiter: ''
+ BasedOnStyle: google
+ - Language: TextProto
+ Delimiters:
+ - pb
+ - PB
+ - proto
+ - PROTO
+ EnclosingFunctions:
+ - EqualsProto
+ - EquivToProto
+ - PARSE_PARTIAL_TEXT_PROTO
+ - PARSE_TEST_PROTO
+ - PARSE_TEXT_PROTO
+ - ParseTextOrDie
+ - ParseTextProtoOrDie
+ - ParseTestProto
+ - ParsePartialTestProto
+ CanonicalDelimiter: pb
+ BasedOnStyle: google
+ReferenceAlignment: Pointer
+ReflowComments: Always
+RemoveBracesLLVM: false
+RemoveEmptyLinesInUnwrappedLines: false
+RemoveParentheses: Leave
+RemoveSemicolon: false
+RequiresClausePosition: OwnLine
+RequiresExpressionIndentation: OuterScope
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SkipMacroDefinitionBody: false
+SortIncludes:
+ Enabled: true
+ IgnoreCase: false
+SortJavaStaticImport: Before
+SortUsingDeclarations: LexicographicNumeric
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterOperatorKeyword: false
+SpaceAfterTemplateKeyword: true
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeJsonColon: false
+SpaceBeforeParens: ControlStatements
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDefinitionName: false
+ AfterFunctionDeclarationName: false
+ AfterIfMacros: true
+ AfterNot: false
+ AfterOverloadedOperator: false
+ AfterPlacementOperator: true
+ AfterRequiresInClause: false
+ AfterRequiresInExpression: false
+ BeforeNonEmptyParentheses: false
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: Never
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
+SpacesInParens: Never
+SpacesInParensOptions:
+ ExceptDoubleParentheses: false
+ InCStyleCasts: false
+ InConditionalStatements: false
+ InEmptyParentheses: false
+ Other: false
+SpacesInSquareBrackets: false
+Standard: Auto
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TableGenBreakInsideDAGArg: DontBreak
+TabWidth: 8
+UseTab: Never
+VerilogBreakBetweenInstancePorts: true
+WhitespaceSensitiveMacros:
+ - BOOST_PP_STRINGIZE
+ - CF_SWIFT_NAME
+ - NS_SWIFT_NAME
+ - PP_STRINGIZE
+ - STRINGIZE
+WrapNamespaceBodyWithEmptyLines: Leave
+...
+
diff --git a/.dep.export b/.dep.export
@@ -1 +1,2 @@
include/finwo/scheduler.h src/scheduler.h
+include/finwo/sched-sem.h src/sched-sem.h
diff --git a/README.md b/README.md
@@ -33,6 +33,7 @@ gcc -o greet examples/example_basic.c src/scheduler.c && ./greet
- **Minimal** — ~120 lines of C, single header + single source file
- **No dependencies** — only `<stdint.h>` and `<sys/select.h>`
- **I/O multiplexing** — built-in `select(2)` integration via `sched_has_data()`
+- **Semaphores** — cooperative synchronization via `sched_sem_*()` primitives
- **Cross-project friendly** — zero-configuration include via `config.mk`
- **Extensible** — task `udata` lets you attach arbitrary state
@@ -167,6 +168,80 @@ int ready = sched_has_data(fds); // returns first ready FD, or -1 if none
- **Returns** — the first ready FD from the array, or `-1` if none are ready
+### Semaphores
+
+Semaphores provide a lightweight synchronization primitive for coordinating tasks. The `value` field can represent signal counts, available bytes, or any resource count you choose.
+
+```c
+#include "src/sched-sem.h"
+
+struct sched_sem {
+ int value;
+};
+```
+
+> [!NOTE]
+> Semaphores are allocated by the user on the stack or heap. The scheduler does not manage their lifetime.
+
+### `sched_sem_init`
+
+```c
+void sched_sem_init(struct sched_sem *sem, int amount);
+```
+
+Set the semaphore to an initial value.
+
+```c
+struct sched_sem mysem;
+sched_sem_init(&mysem, 5); // Start with 5 available
+```
+
+### `sched_sem_signal`
+
+```c
+void sched_sem_signal(struct sched_sem *sem, int amount);
+```
+
+Increment the semaphore by `amount`. Use to produce resources or signal events.
+
+```c
+sched_sem_signal(&mysem, 1); // Add 1 to the count
+```
+
+### `sched_sem_consume`
+
+```c
+void sched_sem_consume(struct sched_sem *sem, int amount);
+```
+
+Decrement the semaphore by `amount`. Use to consume resources.
+
+```c
+sched_sem_consume(&mysem, 1); // Take 1 from the count
+```
+
+### `sched_sem_wait`
+
+```c
+#define sched_sem_wait(sem) ...
+```
+
+A macro that blocks the current task if the semaphore is `<= 0`. If `value > 0`, the macro falls through and the task continues. If `value <= 0`, it returns `SCHED_RUNNING` to defer to the next tick.
+
+```c
+static int worker(int64_t ts, pt_task_t *task) {
+ MyCtx *ctx = task->udata;
+
+ sched_sem_wait(&ctx->sem); // Wait for availability
+ // If we get here, sem.value > 0
+
+ sched_sem_consume(&ctx->sem, 1); // Consume one unit
+ // ... do work ...
+
+ return SCHED_RUNNING;
+}
+```
+
---
## Usage Examples
@@ -180,6 +255,7 @@ Each example is a complete, compilable program.
| [example_io.c](examples/example_io.c) | I/O monitoring with `sched_has_data()` |
| [example_removal.c](examples/example_removal.c) | Parent removes child task via returned `pt_task_t*` |
| [example_multi.c](examples/example_multi.c) | Multiple coordinated tasks sharing state |
+| [example_sem.c](examples/example_sem.c) | Semaphore-based producer/consumer coordination |
See [examples/README.md](examples/README.md) for build instructions.
@@ -215,6 +291,7 @@ The scheduler uses `select(2)` rather than `poll(2)` or `epoll(7)`. This keeps t
|---|---|---|
| `sched_create()` → `udata` pointer | **Caller** | The scheduler never touches your `udata`. You must `free()` it yourself, typically in a cleanup task or after `sched_main()` returns. |
| `sched_create()` → `pt_task_t` struct | **`sched_remove()`** | Freed automatically when the task returns `SCHED_DONE` or `SCHED_ERROR`, or when explicitly removed. |
+| `sched_sem` struct | **Caller** | Semaphores are allocated by the user. The scheduler does not manage their lifetime. |
**Pattern for owned `udata`:**
diff --git a/config.mk b/config.mk
@@ -1 +1,2 @@
SRC+={{module.dirname}}/src/scheduler.c
+SRC+={{module.dirname}}/src/sched-sem.c
diff --git a/examples/.gitignore b/examples/.gitignore
@@ -3,4 +3,5 @@
/example_multi
/example_periodic
/example_removal
+/example_sem
/*.dSYM/
diff --git a/examples/Makefile b/examples/Makefile
@@ -2,7 +2,7 @@ CC ?= gcc
CFLAGS ?= -Wall -Wextra -g -I$(shell pwd)/..
LDFLAGS ?=
-EXAMPLES = example_basic example_periodic example_io example_removal example_multi
+EXAMPLES = example_basic example_periodic example_io example_removal example_multi example_sem
.PHONY: all clean
@@ -23,5 +23,8 @@ example_removal: example_removal.c ../src/scheduler.c ../src/scheduler.h
example_multi: example_multi.c ../src/scheduler.c ../src/scheduler.h
$(CC) $(CFLAGS) -o $@ $< ../src/scheduler.c $(LDFLAGS)
+example_sem: example_sem.c ../src/scheduler.c ../src/sched-sem.c ../src/scheduler.h ../src/sched-sem.h
+ $(CC) $(CFLAGS) -o $@ $< ../src/scheduler.c ../src/sched-sem.c $(LDFLAGS)
+
clean:
rm -f $(EXAMPLES)
diff --git a/examples/example_basic.c b/examples/example_basic.c
@@ -1,15 +1,17 @@
-#include "src/scheduler.h"
#include <stdio.h>
#include <stdlib.h>
+#include "src/scheduler.h"
+
static int greet(int64_t ts, pt_task_t *task) {
- (void)ts; (void)task;
- printf("Hello, world!\n");
- return SCHED_DONE;
+ (void)ts;
+ (void)task;
+ printf("Hello, world!\n");
+ return SCHED_DONE;
}
int main(void) {
- sched_create(greet, NULL);
- sched_main();
- return 0;
+ sched_create(greet, NULL);
+ sched_main();
+ return 0;
}
diff --git a/examples/example_io.c b/examples/example_io.c
@@ -1,4 +1,3 @@
-#include "src/scheduler.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -8,87 +7,89 @@
#include <time.h>
#include <unistd.h>
+#include "src/scheduler.h"
+
typedef struct {
- int pipe_fd;
- int fds[2];
- int64_t last_write;
+ int pipe_fd;
+ int fds[2];
+ int64_t last_write;
} WriterCtx;
typedef struct {
- int pipe_fd;
- int fds[2];
- int done;
+ int pipe_fd;
+ int fds[2];
+ int done;
} ReaderCtx;
static int writer_task(int64_t ts, pt_task_t *task) {
- static int count = 0;
- WriterCtx *ctx = task->udata;
-
- if (ts - ctx->last_write >= 200) {
- char msg[32];
- int len = snprintf(msg, sizeof(msg), "message #%d\n", ++count);
- write(ctx->pipe_fd, msg, len);
- ctx->last_write = ts;
-
- if (count >= 3) {
- snprintf(msg, sizeof(msg), "quit\n");
- write(ctx->pipe_fd, msg, 4);
- close(ctx->pipe_fd);
- free(ctx);
- return SCHED_DONE;
- }
+ static int count = 0;
+ WriterCtx *ctx = task->udata;
+
+ if (ts - ctx->last_write >= 200) {
+ char msg[32];
+ int len = snprintf(msg, sizeof(msg), "message #%d\n", ++count);
+ write(ctx->pipe_fd, msg, len);
+ ctx->last_write = ts;
+
+ if (count >= 3) {
+ snprintf(msg, sizeof(msg), "quit\n");
+ write(ctx->pipe_fd, msg, 4);
+ close(ctx->pipe_fd);
+ free(ctx);
+ return SCHED_DONE;
}
+ }
- return SCHED_RUNNING;
+ return SCHED_RUNNING;
}
static int reader_task(int64_t ts, pt_task_t *task) {
- (void)ts;
- ReaderCtx *ctx = task->udata;
-
- int ready = sched_has_data(ctx->fds);
- if (ready >= 0) {
- char buf[256];
- ssize_t n = read(ready, buf, sizeof(buf) - 1);
- if (n > 0) {
- buf[n] = '\0';
- printf("[reader] %s", buf);
- }
- if (n == 0 || (n > 0 && strncmp(buf, "quit\n", 5) == 0)) {
- ctx->done = 1;
- }
- }
+ (void)ts;
+ ReaderCtx *ctx = task->udata;
- if (ctx->done) {
- close(ctx->pipe_fd);
- free(ctx);
- return SCHED_DONE;
+ int ready = sched_has_data(ctx->fds);
+ if (ready >= 0) {
+ char buf[256];
+ ssize_t n = read(ready, buf, sizeof(buf) - 1);
+ if (n > 0) {
+ buf[n] = '\0';
+ printf("[reader] %s", buf);
+ }
+ if (n == 0 || (n > 0 && strncmp(buf, "quit\n", 5) == 0)) {
+ ctx->done = 1;
}
+ }
- return SCHED_RUNNING;
+ if (ctx->done) {
+ close(ctx->pipe_fd);
+ free(ctx);
+ return SCHED_DONE;
+ }
+
+ return SCHED_RUNNING;
}
int main(void) {
- int pipefd[2];
- if (pipe(pipefd) < 0) {
- perror("pipe");
- return 1;
- }
+ int pipefd[2];
+ if (pipe(pipefd) < 0) {
+ perror("pipe");
+ return 1;
+ }
- WriterCtx *wctx = calloc(1, sizeof(WriterCtx));
- wctx->pipe_fd = pipefd[1];
+ WriterCtx *wctx = calloc(1, sizeof(WriterCtx));
+ wctx->pipe_fd = pipefd[1];
- ReaderCtx *rctx = calloc(1, sizeof(ReaderCtx));
- rctx->pipe_fd = pipefd[0];
- rctx->fds[0] = 1;
- rctx->fds[1] = rctx->pipe_fd;
+ ReaderCtx *rctx = calloc(1, sizeof(ReaderCtx));
+ rctx->pipe_fd = pipefd[0];
+ rctx->fds[0] = 1;
+ rctx->fds[1] = rctx->pipe_fd;
- struct timeval now;
- gettimeofday(&now, NULL);
- wctx->last_write = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ wctx->last_write = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
- sched_create(writer_task, wctx);
- sched_create(reader_task, rctx);
- sched_main();
- return 0;
+ sched_create(writer_task, wctx);
+ sched_create(reader_task, rctx);
+ sched_main();
+ return 0;
}
diff --git a/examples/example_multi.c b/examples/example_multi.c
@@ -1,4 +1,3 @@
-#include "src/scheduler.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -6,100 +5,102 @@
#include <sys/time.h>
#include <unistd.h>
+#include "src/scheduler.h"
+
#define BUF_SIZE 64
typedef struct {
- int reader_fd;
- int writer_fd;
- int fds_reader[2];
- char buf[BUF_SIZE];
- int done;
- int64_t last_write;
+ int reader_fd;
+ int writer_fd;
+ int fds_reader[2];
+ char buf[BUF_SIZE];
+ int done;
+ int64_t last_write;
} SharedCtx;
static int reader_task(int64_t ts, pt_task_t *task) {
- (void)ts;
- SharedCtx *ctx = task->udata;
-
- int ready = sched_has_data(ctx->fds_reader);
- if (ready >= 0) {
- ssize_t n = read(ready, ctx->buf, BUF_SIZE - 1);
- if (n > 0) {
- ctx->buf[n] = '\0';
- printf("[reader] received: \"%s\"\n", ctx->buf);
- }
- if (n == 0 || (n > 0 && strncmp(ctx->buf, "done\n", 5) == 0)) {
- ctx->done = 1;
- }
+ (void)ts;
+ SharedCtx *ctx = task->udata;
+
+ int ready = sched_has_data(ctx->fds_reader);
+ if (ready >= 0) {
+ ssize_t n = read(ready, ctx->buf, BUF_SIZE - 1);
+ if (n > 0) {
+ ctx->buf[n] = '\0';
+ printf("[reader] received: \"%s\"\n", ctx->buf);
}
-
- if (ctx->done) {
- close(ctx->reader_fd);
- ctx->reader_fd = -1;
- return SCHED_DONE;
+ if (n == 0 || (n > 0 && strncmp(ctx->buf, "done\n", 5) == 0)) {
+ ctx->done = 1;
}
+ }
+
+ if (ctx->done) {
+ close(ctx->reader_fd);
+ ctx->reader_fd = -1;
+ return SCHED_DONE;
+ }
- return SCHED_RUNNING;
+ return SCHED_RUNNING;
}
static int writer_task(int64_t ts, pt_task_t *task) {
- SharedCtx *ctx = task->udata;
-
- if (ctx->done) {
- return SCHED_DONE;
+ SharedCtx *ctx = task->udata;
+
+ if (ctx->done) {
+ return SCHED_DONE;
+ }
+
+ if (ts - ctx->last_write >= 200) {
+ static int count = 0;
+ char msg[32];
+ int len = snprintf(msg, sizeof(msg), "message #%d\n", ++count);
+ write(ctx->writer_fd, msg, len);
+ ctx->last_write = ts;
+
+ if (count >= 3) {
+ snprintf(msg, sizeof(msg), "done\n");
+ write(ctx->writer_fd, msg, 4);
+ close(ctx->writer_fd);
+ ctx->writer_fd = -1;
}
+ }
- if (ts - ctx->last_write >= 200) {
- static int count = 0;
- char msg[32];
- int len = snprintf(msg, sizeof(msg), "message #%d\n", ++count);
- write(ctx->writer_fd, msg, len);
- ctx->last_write = ts;
-
- if (count >= 3) {
- snprintf(msg, sizeof(msg), "done\n");
- write(ctx->writer_fd, msg, 4);
- close(ctx->writer_fd);
- ctx->writer_fd = -1;
- }
- }
-
- return SCHED_RUNNING;
+ return SCHED_RUNNING;
}
static int shutdown_task(int64_t ts, pt_task_t *task) {
- (void)ts;
- SharedCtx *ctx = task->udata;
+ (void)ts;
+ SharedCtx *ctx = task->udata;
- if (ctx->done && ctx->reader_fd < 0 && ctx->writer_fd < 0) {
- printf("[shutdown] all done, cleaning up.\n");
- free(ctx);
- return SCHED_DONE;
- }
+ if (ctx->done && ctx->reader_fd < 0 && ctx->writer_fd < 0) {
+ printf("[shutdown] all done, cleaning up.\n");
+ free(ctx);
+ return SCHED_DONE;
+ }
- return SCHED_RUNNING;
+ return SCHED_RUNNING;
}
int main(void) {
- int pipefd[2];
- if (pipe(pipefd) < 0) {
- perror("pipe");
- return 1;
- }
-
- SharedCtx *ctx = calloc(1, sizeof(SharedCtx));
- ctx->reader_fd = pipefd[0];
- ctx->writer_fd = pipefd[1];
- ctx->fds_reader[0] = 1;
- ctx->fds_reader[1] = ctx->reader_fd;
-
- struct timeval now;
- gettimeofday(&now, NULL);
- ctx->last_write = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
-
- sched_create(writer_task, ctx);
- sched_create(reader_task, ctx);
- sched_create(shutdown_task, ctx);
- sched_main();
- return 0;
+ int pipefd[2];
+ if (pipe(pipefd) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ SharedCtx *ctx = calloc(1, sizeof(SharedCtx));
+ ctx->reader_fd = pipefd[0];
+ ctx->writer_fd = pipefd[1];
+ ctx->fds_reader[0] = 1;
+ ctx->fds_reader[1] = ctx->reader_fd;
+
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ ctx->last_write = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
+
+ sched_create(writer_task, ctx);
+ sched_create(reader_task, ctx);
+ sched_create(shutdown_task, ctx);
+ sched_main();
+ return 0;
}
diff --git a/examples/example_periodic.c b/examples/example_periodic.c
@@ -1,41 +1,42 @@
-#include "src/scheduler.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
+#include "src/scheduler.h"
+
typedef struct {
- int64_t start;
- int64_t interval_ms;
- int64_t created_at;
+ int64_t start;
+ int64_t interval_ms;
+ int64_t created_at;
} TimerCtx;
static int ticker(int64_t ts, pt_task_t *task) {
- TimerCtx *ctx = task->udata;
+ TimerCtx *ctx = task->udata;
- if (ts - ctx->start >= ctx->interval_ms) {
- printf("[%lld ms] tick!\n", (long long)(ts - ctx->created_at));
- ctx->start = ts;
+ if (ts - ctx->start >= ctx->interval_ms) {
+ printf("[%lld ms] tick!\n", (long long)(ts - ctx->created_at));
+ ctx->start = ts;
- if (ts - ctx->created_at >= 5000) {
- printf("5 seconds elapsed, stopping.\n");
- free(ctx);
- return SCHED_DONE;
- }
+ if (ts - ctx->created_at >= 5000) {
+ printf("5 seconds elapsed, stopping.\n");
+ free(ctx);
+ return SCHED_DONE;
}
+ }
- return SCHED_RUNNING;
+ return SCHED_RUNNING;
}
int main(void) {
- TimerCtx *ctx = calloc(1, sizeof(TimerCtx));
+ TimerCtx *ctx = calloc(1, sizeof(TimerCtx));
- struct timeval now;
- gettimeofday(&now, NULL);
- ctx->created_at = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
- ctx->start = ctx->created_at;
- ctx->interval_ms = 1000;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ ctx->created_at = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
+ ctx->start = ctx->created_at;
+ ctx->interval_ms = 1000;
- sched_create(ticker, ctx);
- sched_main();
- return 0;
+ sched_create(ticker, ctx);
+ sched_main();
+ return 0;
}
diff --git a/examples/example_removal.c b/examples/example_removal.c
@@ -1,65 +1,66 @@
-#include "src/scheduler.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
+#include "src/scheduler.h"
+
typedef struct {
- int64_t start;
- int ticks;
- int removed;
+ int64_t start;
+ int ticks;
+ int removed;
} ChildCtx;
typedef struct {
- int64_t start;
- int64_t max_age_ms;
- ChildCtx *child_ctx;
- int ticks;
+ int64_t start;
+ int64_t max_age_ms;
+ ChildCtx *child_ctx;
+ int ticks;
} ParentCtx;
static int child_task(int64_t ts, pt_task_t *task) {
- ChildCtx *ctx = task->udata;
- if (ctx->removed) {
- free(ctx);
- return SCHED_DONE;
- }
- ctx->ticks++;
- printf(" [child] tick %d (age: %lld ms)\n", ctx->ticks, (long long)(ts - ctx->start));
- return SCHED_RUNNING;
+ ChildCtx *ctx = task->udata;
+ if (ctx->removed) {
+ free(ctx);
+ return SCHED_DONE;
+ }
+ ctx->ticks++;
+ printf(" [child] tick %d (age: %lld ms)\n", ctx->ticks, (long long)(ts - ctx->start));
+ return SCHED_RUNNING;
}
static int parent_task(int64_t ts, pt_task_t *task) {
- ParentCtx *ctx = task->udata;
- ctx->ticks++;
- int64_t age = ts - ctx->start;
- printf("[parent] tick %d (elapsed: %lld ms)\n", ctx->ticks, (long long)age);
+ ParentCtx *ctx = task->udata;
+ ctx->ticks++;
+ int64_t age = ts - ctx->start;
+ printf("[parent] tick %d (elapsed: %lld ms)\n", ctx->ticks, (long long)age);
- if (age >= ctx->max_age_ms) {
- printf("[parent] child is old enough, signaling it to stop.\n");
- ctx->child_ctx->removed = 1;
- free(ctx);
- return SCHED_DONE;
- }
+ if (age >= ctx->max_age_ms) {
+ printf("[parent] child is old enough, signaling it to stop.\n");
+ ctx->child_ctx->removed = 1;
+ free(ctx);
+ return SCHED_DONE;
+ }
- return SCHED_RUNNING;
+ return SCHED_RUNNING;
}
int main(void) {
- struct timeval now;
- gettimeofday(&now, NULL);
- int64_t ts = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ int64_t ts = (int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
- ChildCtx *child_ctx = calloc(1, sizeof(ChildCtx));
- child_ctx->start = ts;
+ ChildCtx *child_ctx = calloc(1, sizeof(ChildCtx));
+ child_ctx->start = ts;
- ParentCtx *parent_ctx = calloc(1, sizeof(ParentCtx));
- parent_ctx->start = ts;
- parent_ctx->max_age_ms = 2500;
- parent_ctx->child_ctx = child_ctx;
+ ParentCtx *parent_ctx = calloc(1, sizeof(ParentCtx));
+ parent_ctx->start = ts;
+ parent_ctx->max_age_ms = 2500;
+ parent_ctx->child_ctx = child_ctx;
- sched_create(child_task, child_ctx);
- sched_create(parent_task, parent_ctx);
- sched_main();
+ sched_create(child_task, child_ctx);
+ sched_create(parent_task, parent_ctx);
+ sched_main();
- printf("Both tasks are gone. Goodbye.\n");
- return 0;
+ printf("Both tasks are gone. Goodbye.\n");
+ return 0;
}
diff --git a/examples/example_sem.c b/examples/example_sem.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "src/sched-sem.h"
+#include "src/scheduler.h"
+
+typedef struct {
+ struct sched_sem sem;
+ int tokens_produced;
+ int tokens_consumed;
+} ProducerCtx;
+
+typedef struct {
+ struct sched_sem *sem;
+ int *tokens_consumed;
+} ConsumerCtx;
+
+static int producer(int64_t ts, pt_task_t *task) {
+ ProducerCtx *ctx = task->udata;
+ (void)ts;
+
+ sched_sem_signal(&ctx->sem, 1);
+ ctx->tokens_produced++;
+
+ if (ctx->tokens_produced >= 10) {
+ printf("Producer done (produced %d tokens)\n", ctx->tokens_produced);
+ return SCHED_DONE;
+ }
+ return SCHED_RUNNING;
+}
+
+static int consumer(int64_t ts, pt_task_t *task) {
+ ConsumerCtx *ctx = task->udata;
+ (void)ts;
+
+ sched_sem_wait(ctx->sem);
+ (*ctx->tokens_consumed)++;
+
+ if (*ctx->tokens_consumed >= 10) {
+ printf("Consumer done (consumed %d tokens)\n", *ctx->tokens_consumed);
+ return SCHED_DONE;
+ }
+ return SCHED_RUNNING;
+}
+
+int main(void) {
+ ProducerCtx producer_ctx = {.sem = {.value = 0}, .tokens_produced = 0};
+ int tokens_consumed = 0;
+ ConsumerCtx consumer_ctx = {.sem = &producer_ctx.sem, .tokens_consumed = &tokens_consumed};
+
+ sched_create(producer, &producer_ctx);
+ sched_create(consumer, &consumer_ctx);
+ sched_main();
+
+ return 0;
+}
diff --git a/src/sched-sem.c b/src/sched-sem.c
@@ -0,0 +1,13 @@
+#include "sched-sem.h"
+
+void sched_sem_init(struct sched_sem *sem, int amount) {
+ sem->value = amount;
+}
+
+void sched_sem_signal(struct sched_sem *sem, int amount) {
+ sem->value += amount;
+}
+
+void sched_sem_consume(struct sched_sem *sem, int amount) {
+ sem->value -= amount;
+}
diff --git a/src/sched-sem.h b/src/sched-sem.h
@@ -0,0 +1,17 @@
+#ifndef __FINWO_SCHEDULER_SEM_H__
+#define __FINWO_SCHEDULER_SEM_H__
+
+struct sched_sem {
+ int value;
+};
+
+void sched_sem_init(struct sched_sem *sem, int amount);
+void sched_sem_signal(struct sched_sem *sem, int amount);
+void sched_sem_consume(struct sched_sem *sem, int amount);
+
+#define sched_sem_wait(sem) \
+ do { \
+ if ((sem)->value <= 0) return SCHED_RUNNING; \
+ } while (0)
+
+#endif // __FINWO_SCHEDULER_SEM_H__