Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion kernel/sched/sched.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ __PRIVILEGED_CODE task* create_user_thread(
t->exec.system_stack_top = sys_stack_top;
t->exec.pt_root = paging::supervisor_pt_root_for_user_task(creator->exec.mm_ctx->pt_root);
t->exec.user_pt_root = creator->exec.mm_ctx->pt_root;
t->exec.tls_base = 0;
t->exec.tls_base = creator->exec.tls_base;
t->task_stack_base = 0; // user stack is not VMM-allocated
t->sys_stack_base = sys_stack_base;

Expand Down
2 changes: 1 addition & 1 deletion userland/apps/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
APP_DIRS := init hello shell ls cat rm stat touch sleep true false clear ptytest date \
clockbench stlxdm stlxterm doom ping ifconfig nslookup arp udpecho tcpecho \
fetch polltest dropbear blackjack wordle hangman snake tetris \
grep wc head threadtest uname kill cxxtest
grep wc head threadtest uname kill cxxtest synctest
APP_COUNT := $(words $(APP_DIRS))

all:
Expand Down
3 changes: 3 additions & 0 deletions userland/apps/synctest/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
APP_NAME := synctest
APP_LIBS := stlxcxx
include ../../mk/cxxapp.mk
209 changes: 209 additions & 0 deletions userland/apps/synctest/src/synctest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <stlxstd/thread.h>
#include <stlxstd/mutex.h>
#include <stlxstd/condition_variable.h>
#include <stlxstd/barrier.h>

static int g_passed = 0;
static int g_failed = 0;

static void check(const char* name, bool ok) {
printf(" %s: %s\n", ok ? "PASS" : "FAIL", name);
if (ok) g_passed++;
else g_failed++;
}

// --- Test 1: Mutex stress ---

static constexpr int MUTEX_THREADS = 8;
static constexpr int MUTEX_ITERS = 10000;

static void test_mutex_stress() {
stlxstd::mutex mtx;
int counter = 0;

stlxstd::thread threads[MUTEX_THREADS];
for (int i = 0; i < MUTEX_THREADS; i++) {
threads[i] = stlxstd::thread([&] {
for (int j = 0; j < MUTEX_ITERS; j++) {
stlxstd::lock_guard<stlxstd::mutex> guard(mtx);
counter++;
}
});
}
for (int i = 0; i < MUTEX_THREADS; i++) {
threads[i].join();
}

check("mutex stress (8 threads x 10000)", counter == MUTEX_THREADS * MUTEX_ITERS);
}

// --- Test 2: Condition variable producer/consumer ---

static constexpr int ITEMS = 100;

static void test_condvar_producer_consumer() {
stlxstd::mutex mtx;
stlxstd::condition_variable cv;
int produced = 0;
int consumed = 0;
bool done = false;

stlxstd::thread consumer([&] {
stlxstd::unique_lock<stlxstd::mutex> lock(mtx);
while (!done || produced > consumed) {
cv.wait(lock, [&] { return produced > consumed || done; });
while (produced > consumed) {
consumed++;
}
}
});

stlxstd::thread producer([&] {
for (int i = 0; i < ITEMS; i++) {
{
stlxstd::lock_guard<stlxstd::mutex> guard(mtx);
produced++;
}
cv.notify_one();
}
{
stlxstd::lock_guard<stlxstd::mutex> guard(mtx);
done = true;
}
cv.notify_one();
});

producer.join();
consumer.join();

check("condvar producer/consumer (100 items)", consumed == ITEMS);
}

// --- Test 3: Barrier synchronization ---

static constexpr int BARRIER_THREADS = 4;

static void test_barrier() {
stlxstd::barrier bar(BARRIER_THREADS);
volatile int flags[BARRIER_THREADS] = {};
volatile int verified[BARRIER_THREADS] = {};

stlxstd::thread threads[BARRIER_THREADS];
for (int i = 0; i < BARRIER_THREADS; i++) {
threads[i] = stlxstd::thread([&, i] {
__atomic_store_n(&flags[i], 1, __ATOMIC_RELEASE);
bar.arrive_and_wait();
// After barrier: all flags must be set
bool all_set = true;
for (int j = 0; j < BARRIER_THREADS; j++) {
if (!__atomic_load_n(&flags[j], __ATOMIC_ACQUIRE)) {
all_set = false;
}
}
__atomic_store_n(&verified[i], all_set ? 1 : 0, __ATOMIC_RELEASE);
});
}
for (int i = 0; i < BARRIER_THREADS; i++) {
threads[i].join();
}

bool ok = true;
for (int i = 0; i < BARRIER_THREADS; i++) {
if (!verified[i]) ok = false;
}
check("barrier (4 threads)", ok);
}

// --- Test 4: Thread join ---

static void test_thread_join() {
volatile int value = 0;

stlxstd::thread t([&] {
__atomic_store_n(&value, 42, __ATOMIC_RELEASE);
});
t.join();

check("thread join", __atomic_load_n(&value, __ATOMIC_ACQUIRE) == 42);
}

// --- Test 5: Thread detach ---

static void test_thread_detach() {
volatile int flag = 0;

{
stlxstd::thread t([&] {
__atomic_store_n(&flag, 1, __ATOMIC_RELEASE);
});
t.detach();
}

// Brief busy-wait for the detached thread to run
for (int i = 0; i < 10000000; i++) {
if (__atomic_load_n(&flag, __ATOMIC_ACQUIRE)) break;
asm volatile("" ::: "memory");
}

check("thread detach", __atomic_load_n(&flag, __ATOMIC_ACQUIRE) == 1);
}

// --- Test 6: Multiple independent mutexes ---

static constexpr int MULTI_THREADS = 4;
static constexpr int MULTI_ITERS = 5000;

static void test_multi_mutex() {
stlxstd::mutex mtx_a, mtx_b;
int counter_a = 0;
int counter_b = 0;

stlxstd::thread threads[MULTI_THREADS];
for (int i = 0; i < MULTI_THREADS; i++) {
threads[i] = stlxstd::thread([&] {
for (int j = 0; j < MULTI_ITERS; j++) {
{
stlxstd::lock_guard<stlxstd::mutex> guard(mtx_a);
counter_a++;
}
{
stlxstd::lock_guard<stlxstd::mutex> guard(mtx_b);
counter_b++;
}
}
});
}
for (int i = 0; i < MULTI_THREADS; i++) {
threads[i].join();
}

bool ok = (counter_a == MULTI_THREADS * MULTI_ITERS) &&
(counter_b == MULTI_THREADS * MULTI_ITERS);
check("multi-mutex (2 locks, 4 threads x 5000)", ok);
}

int main() {
printf("\nsynctest: Stellux synchronization test suite\n\n");

printf("[mutex]\n");
test_mutex_stress();

printf("\n[condition variable]\n");
test_condvar_producer_consumer();

printf("\n[barrier]\n");
test_barrier();

printf("\n[thread lifecycle]\n");
test_thread_join();
test_thread_detach();

printf("\n[multi-mutex]\n");
test_multi_mutex();

printf("\n--- Results: %d passed, %d failed ---\n\n", g_passed, g_failed);
return g_failed > 0 ? 1 : 0;
}
2 changes: 1 addition & 1 deletion userland/lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Stellux Userland - Libraries
#

LIB_DIRS := libstlx libstlxgfx libbearssl
LIB_DIRS := libstlx libstlxcxx libstlxgfx libbearssl
LIB_COUNT := $(words $(LIB_DIRS))

all:
Expand Down
25 changes: 25 additions & 0 deletions userland/lib/libstlx/include/stlx/barrier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef STLX_BARRIER_H
#define STLX_BARRIER_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
uint64_t count;
uint32_t generation;
uint32_t total;
} stlx_barrier_t;

void stlx_barrier_init(stlx_barrier_t* b, uint32_t count);

/* Block until all count threads have called barrier_wait. Reusable. */
void stlx_barrier_wait(stlx_barrier_t* b);

#ifdef __cplusplus
}
#endif

#endif /* STLX_BARRIER_H */
29 changes: 29 additions & 0 deletions userland/lib/libstlx/include/stlx/cond.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef STLX_COND_H
#define STLX_COND_H

#include <stdint.h>
#include <stlx/mutex.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct { uint32_t seq; } stlx_cond_t;

#define STLX_COND_INIT { 0 }

/* Atomically unlocks the mutex and sleeps until signaled, then re-locks.
* Callers must re-check the predicate in a loop (spurious wakeups allowed). */
void stlx_cond_wait(stlx_cond_t* cv, stlx_mutex_t* m);

/* Wake one waiter. */
void stlx_cond_signal(stlx_cond_t* cv);

/* Wake all waiters. */
void stlx_cond_broadcast(stlx_cond_t* cv);

#ifdef __cplusplus
}
#endif

#endif /* STLX_COND_H */
24 changes: 24 additions & 0 deletions userland/lib/libstlx/include/stlx/futex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef STLX_FUTEX_H
#define STLX_FUTEX_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* Block if *addr == expected. timeout_ns=0 waits indefinitely.
* Returns 0 on wake, negative errno on error (-EAGAIN, -ETIMEDOUT). */
int stlx_futex_wait(uint32_t* addr, uint32_t expected, uint64_t timeout_ns);

/* Wake up to count threads waiting on addr. Returns number woken. */
int stlx_futex_wake(uint32_t* addr, uint32_t count);

/* Wake all threads waiting on addr. Returns number woken. */
int stlx_futex_wake_all(uint32_t* addr);

#ifdef __cplusplus
}
#endif

#endif /* STLX_FUTEX_H */
25 changes: 25 additions & 0 deletions userland/lib/libstlx/include/stlx/mutex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef STLX_MUTEX_H
#define STLX_MUTEX_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* State 0: unlocked, 1: locked (no waiters), 2: locked (with waiters) */
typedef struct { uint32_t state; } stlx_mutex_t;

#define STLX_MUTEX_INIT { 0 }

void stlx_mutex_lock(stlx_mutex_t* m);
void stlx_mutex_unlock(stlx_mutex_t* m);

/* Returns 0 if acquired, -1 if already held. */
int stlx_mutex_trylock(stlx_mutex_t* m);

#ifdef __cplusplus
}
#endif

#endif /* STLX_MUTEX_H */
8 changes: 8 additions & 0 deletions userland/lib/libstlx/include/stlx/proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* Wait status decode macros (Linux-compatible bit layout). */
#define STLX_WIFEXITED(s) (((s) & 0x7F) == 0)
#define STLX_WEXITSTATUS(s) (((s) >> 8) & 0xFF)
Expand Down Expand Up @@ -96,4 +100,8 @@ static inline int proc_thread_join(int handle, int* exit_code) { return proc_wai
static inline int proc_thread_detach(int handle) { return proc_detach(handle); }
static inline int proc_thread_kill(int handle) { return proc_kill(handle); }

#ifdef __cplusplus
}
#endif

#endif /* STLX_PROC_H */
4 changes: 4 additions & 0 deletions userland/lib/libstlx/include/stlx/syscall_nums.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@
#define SYS_PROC_KILL_TID 1018
#define SYS_PTY_CREATE 1020

#define SYS_FUTEX_WAIT 1030
#define SYS_FUTEX_WAKE 1031
#define SYS_FUTEX_WAKE_ALL 1032

#endif /* STLX_SYSCALL_NUMS_H */
Loading
Loading