Skip to content

Commit 1f273e0

Browse files
committed
Update docs, update telnet example to use pthread_create.c
1 parent ed8e174 commit 1f273e0

5 files changed

Lines changed: 23 additions & 16 deletions

File tree

docs/syscalls.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ u64 thread_spawn(void* fptr, void* arg)
8484

8585
**Returns:** `u64 tid`
8686

87-
Spawn a new thread running the given function with the argument value `arg`.
87+
Spawn a new thread running the given function with the argument value `arg`. This is a low-level primitive: the spawned thread has no software (alloca) stack, so a C thread function that uses local arrays, address-taken locals, or alloca will fault. From C, prefer pthread_create() from <pthread.h>, which installs a private stack for the new thread.
8888

8989
## thread_id
9090

@@ -112,7 +112,7 @@ u64 thread_join(u64 tid)
112112

113113
**Returns:** `u64 val`
114114

115-
Join on the thread with the given id. Produces the return value for the thread.
115+
Join on the thread with the given id (as returned by thread_spawn). Produces the return value for the thread. From C, prefer pthread_join() from <pthread.h> when the thread was created with pthread_create().
116116

117117
## cmd_argc
118118

ncc/include/uvm/syscalls.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extern void __uvm_exit(int8_t __status);
5050
#define exit(__status) __uvm_exit(__status)
5151

5252
// u64 thread_spawn(void* fptr, void* arg)
53-
// Spawn a new thread running the given function with the argument value `arg`.
53+
// Spawn a new thread running the given function with the argument value `arg`. This is a low-level primitive: the spawned thread has no software (alloca) stack, so a C thread function that uses local arrays, address-taken locals, or alloca will fault. From C, prefer pthread_create() from <pthread.h>, which installs a private stack for the new thread.
5454
extern uint64_t __uvm_thread_spawn(void* __fptr, void* __arg);
5555
#define thread_spawn(__fptr, __arg) __uvm_thread_spawn(__fptr, __arg)
5656

@@ -65,7 +65,7 @@ extern void __uvm_thread_sleep(uint64_t __time_ms);
6565
#define thread_sleep(__time_ms) __uvm_thread_sleep(__time_ms)
6666

6767
// u64 thread_join(u64 tid)
68-
// Join on the thread with the given id. Produces the return value for the thread.
68+
// Join on the thread with the given id (as returned by thread_spawn). Produces the return value for the thread. From C, prefer pthread_join() from <pthread.h> when the thread was created with pthread_create().
6969
extern uint64_t __uvm_thread_join(uint64_t __tid);
7070
#define thread_join(__tid) __uvm_thread_join(__tid)
7171

@@ -261,7 +261,7 @@ extern uint64_t __uvm_file_size(uint64_t __handle);
261261
#define exit(__status) asm (__status) -> void { syscall exit; }
262262

263263
// u64 thread_spawn(void* fptr, void* arg)
264-
// Spawn a new thread running the given function with the argument value `arg`.
264+
// Spawn a new thread running the given function with the argument value `arg`. This is a low-level primitive: the spawned thread has no software (alloca) stack, so a C thread function that uses local arrays, address-taken locals, or alloca will fault. From C, prefer pthread_create() from <pthread.h>, which installs a private stack for the new thread.
265265
#define thread_spawn(__fptr, __arg) asm (__fptr, __arg) -> u64 { syscall thread_spawn; }
266266

267267
// u64 thread_id()
@@ -273,7 +273,7 @@ extern uint64_t __uvm_file_size(uint64_t __handle);
273273
#define thread_sleep(__time_ms) asm (__time_ms) -> void { syscall thread_sleep; }
274274

275275
// u64 thread_join(u64 tid)
276-
// Join on the thread with the given id. Produces the return value for the thread.
276+
// Join on the thread with the given id (as returned by thread_spawn). Produces the return value for the thread. From C, prefer pthread_join() from <pthread.h> when the thread was created with pthread_create().
277277
#define thread_join(__tid) asm (__tid) -> u64 { syscall thread_join; }
278278

279279
// u64 cmd_argc()

spec/syscalls.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
],
161161
"permission": "default_allowed",
162162
"const_idx": 29,
163-
"description": "Spawn a new thread running the given function with the argument value `arg`."
163+
"description": "Spawn a new thread running the given function with the argument value `arg`. This is a low-level primitive: the spawned thread has no software (alloca) stack, so a C thread function that uses local arrays, address-taken locals, or alloca will fault. From C, prefer pthread_create() from <pthread.h>, which installs a private stack for the new thread."
164164
},
165165
{
166166
"name": "thread_id",
@@ -203,7 +203,7 @@
203203
],
204204
"permission": "default_allowed",
205205
"const_idx": 31,
206-
"description": "Join on the thread with the given id. Produces the return value for the thread."
206+
"description": "Join on the thread with the given id (as returned by thread_spawn). Produces the return value for the thread. From C, prefer pthread_join() from <pthread.h> when the thread was created with pthread_create()."
207207
},
208208
{
209209
"name": "cmd_argc",

uvclang/examples/telnet_server.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,25 @@
33
//
44
// This shows the blocking UVM networking API: net_accept blocks until a client
55
// connects, and net_read blocks until data arrives. To serve several clients
6-
// at once, the server spawns one thread per connection instead of relying on
6+
// at once, the server spawns one pthread per connection instead of relying on
77
// callbacks. Fallible calls report errors with negative return values
88
// (NET_ERROR, NET_TIMEOUT); see <uvm/syscalls.h>.
99
//
1010

1111
#include <stdint.h>
1212
#include <stdio.h>
1313
#include <string.h>
14+
#include <pthread.h>
1415
#include <uvm/syscalls.h>
1516

1617
// Handle a single client connection.
1718
//
18-
// One instance of this function runs per client, each on its own thread, so
19+
// One instance of this function runs per client, each on its own pthread, so
1920
// multiple clients can be served concurrently. The server echoes a reply for
2021
// each message and closes the connection when the client sends "exit".
21-
uint64_t handle_conn(uint64_t sock)
22+
void *handle_conn(void *arg)
2223
{
24+
int64_t sock = (int64_t)arg;
2325
char read_buf[1024];
2426

2527
for (;;)
@@ -85,7 +87,12 @@ int main(void)
8587
}
8688

8789
printf("new connection from %s\n", client_addr);
88-
thread_spawn((void *)handle_conn, (void *)conn_sock);
90+
91+
// Serve this client on its own thread. pthread_create gives the thread a
92+
// private stack for handle_conn's locals (read_buf, etc.); the raw
93+
// thread_spawn syscall does not, so alloca-bearing code must use pthreads.
94+
pthread_t thread;
95+
pthread_create(&thread, NULL, handle_conn, (void *)conn_sock);
8996
}
9097

9198
return 0;

uvclang/include/uvm/syscalls.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extern void __uvm_exit(int8_t __status);
5050
#define exit(__status) __uvm_exit(__status)
5151

5252
// u64 thread_spawn(void* fptr, void* arg)
53-
// Spawn a new thread running the given function with the argument value `arg`.
53+
// Spawn a new thread running the given function with the argument value `arg`. This is a low-level primitive: the spawned thread has no software (alloca) stack, so a C thread function that uses local arrays, address-taken locals, or alloca will fault. From C, prefer pthread_create() from <pthread.h>, which installs a private stack for the new thread.
5454
extern uint64_t __uvm_thread_spawn(void* __fptr, void* __arg);
5555
#define thread_spawn(__fptr, __arg) __uvm_thread_spawn(__fptr, __arg)
5656

@@ -65,7 +65,7 @@ extern void __uvm_thread_sleep(uint64_t __time_ms);
6565
#define thread_sleep(__time_ms) __uvm_thread_sleep(__time_ms)
6666

6767
// u64 thread_join(u64 tid)
68-
// Join on the thread with the given id. Produces the return value for the thread.
68+
// Join on the thread with the given id (as returned by thread_spawn). Produces the return value for the thread. From C, prefer pthread_join() from <pthread.h> when the thread was created with pthread_create().
6969
extern uint64_t __uvm_thread_join(uint64_t __tid);
7070
#define thread_join(__tid) __uvm_thread_join(__tid)
7171

@@ -261,7 +261,7 @@ extern uint64_t __uvm_file_size(uint64_t __handle);
261261
#define exit(__status) asm (__status) -> void { syscall exit; }
262262

263263
// u64 thread_spawn(void* fptr, void* arg)
264-
// Spawn a new thread running the given function with the argument value `arg`.
264+
// Spawn a new thread running the given function with the argument value `arg`. This is a low-level primitive: the spawned thread has no software (alloca) stack, so a C thread function that uses local arrays, address-taken locals, or alloca will fault. From C, prefer pthread_create() from <pthread.h>, which installs a private stack for the new thread.
265265
#define thread_spawn(__fptr, __arg) asm (__fptr, __arg) -> u64 { syscall thread_spawn; }
266266

267267
// u64 thread_id()
@@ -273,7 +273,7 @@ extern uint64_t __uvm_file_size(uint64_t __handle);
273273
#define thread_sleep(__time_ms) asm (__time_ms) -> void { syscall thread_sleep; }
274274

275275
// u64 thread_join(u64 tid)
276-
// Join on the thread with the given id. Produces the return value for the thread.
276+
// Join on the thread with the given id (as returned by thread_spawn). Produces the return value for the thread. From C, prefer pthread_join() from <pthread.h> when the thread was created with pthread_create().
277277
#define thread_join(__tid) asm (__tid) -> u64 { syscall thread_join; }
278278

279279
// u64 cmd_argc()

0 commit comments

Comments
 (0)