| \c #include <limits.h> |
\c #include <limits.h> |
| \c #include <sys/mman.h> |
\c #include <sys/mman.h> |
| \c #include <unistd.h> |
\c #include <unistd.h> |
| |
\c #include <setjmp.h> |
| |
\c #include <stdio.h> |
| \c #define wholepage(n) (((n)+pagesize-1)&~(pagesize-1)) |
\c #define wholepage(n) (((n)+pagesize-1)&~(pagesize-1)) |
| \c typedef struct { |
\c typedef struct { |
| \c Cell data_stack_size; |
\c Cell next_task; |
| \c Cell fp_stack_size; |
\c Cell prev_task; |
| \c Cell return_stack_size; |
\c Cell save_task; |
| \c Cell locals_stack_size; |
\c Cell sp0, rp0, fp0, lp0; |
| \c Cell sp0, fp0, rp0, lp0; |
\c Cell throw_entry; |
| \c Cell boot_entry; |
\c } user_area; |
| \c Cell saved_ip, saved_rp; |
|
| \c } threadId; |
|
| \c int pagesize = 1; |
\c int pagesize = 1; |
| \c void page_noaccess(void *a) |
\c void page_noaccess(void *a) |
| \c { |
\c { |
| \c return r; |
\c return r; |
| \c } |
\c } |
| \c |
\c |
| \c int gforth_create_thread(threadId * t) |
\c Cell gforth_create_thread(Cell dsize, Cell rsize, Cell fsize, Cell lsize) |
| \c { |
\c { |
| \c #if HAVE_GETPAGESIZE |
\c #if HAVE_GETPAGESIZE |
| \c pagesize=getpagesize(); /* Linux/GNU libc offers this */ |
\c pagesize=getpagesize(); /* Linux/GNU libc offers this */ |
| \c #elif PAGESIZE |
\c #elif PAGESIZE |
| \c pagesize=PAGESIZE; /* in limits.h according to Gallmeister's POSIX.4 book */ |
\c pagesize=PAGESIZE; /* in limits.h according to Gallmeister's POSIX.4 book */ |
| \c #endif |
\c #endif |
| \c Cell dsize = wholepage(t->data_stack_size); |
\c size_t totalsize; |
| \c Cell rsize = wholepage(t->return_stack_size); |
\c Cell a; |
| \c Cell fsize = wholepage(t->fp_stack_size); |
\c user_area * up0; |
| \c Cell lsize = wholepage(t->locals_stack_size); |
\c dsize = wholepage(dsize); |
| \c size_t totalsize = dsize+fsize+rsize+lsize+5*pagesize; |
\c rsize = wholepage(rsize); |
| \c Cell a = (Cell)alloc_mmap(totalsize); |
\c fsize = wholepage(fsize); |
| |
\c lsize = wholepage(lsize); |
| |
\c totalsize = dsize+fsize+rsize+lsize+6*pagesize; |
| |
\c a = (Cell)alloc_mmap(totalsize); |
| \c if (a != (Cell)MAP_FAILED) { |
\c if (a != (Cell)MAP_FAILED) { |
| \c page_noaccess((void*)a); a+=pagesize; a+=dsize; t->sp0=a; |
\c up0=(user_area*)a; a+=pagesize; |
| \c page_noaccess((void*)a); a+=pagesize; a+=fsize; t->fp0=a; |
\c page_noaccess((void*)a); a+=pagesize; a+=dsize; up0->sp0=a; |
| \c page_noaccess((void*)a); a+=pagesize; a+=rsize; t->rp0=a; |
\c page_noaccess((void*)a); a+=pagesize; a+=fsize; up0->fp0=a; |
| \c page_noaccess((void*)a); a+=pagesize; a+=lsize; t->lp0=a; |
\c page_noaccess((void*)a); a+=pagesize; a+=rsize; up0->rp0=a; |
| |
\c page_noaccess((void*)a); a+=pagesize; a+=lsize; up0->lp0=a; |
| \c page_noaccess((void*)a); |
\c page_noaccess((void*)a); |
| \c return 1; |
\c return (Cell)up0; |
| \c } |
\c } |
| \c return 0; |
\c return 0; |
| \c } |
\c } |
| \c |
\c |
| \c void *gforth_thread(threadId * t) |
\c void gforth_cleanup_thread(void * t) |
| \c { |
\c { |
| \c if(gforth_create_thread(t)) { |
\c Cell size = wholepage((Cell)(((user_area*)t)->lp0)+pagesize-(Cell)t); |
| \c return gforth_engine((void*)(t->boot_entry), (Cell*)(t->sp0), (Cell*)(t->rp0), (Float*)(t->fp0), (void*)(t->lp0), (char*)&(t->saved_ip)); |
\c munmap(t, size); |
| \c } |
\c } |
| \c return NULL; |
\c |
| |
\c extern __thread jmp_buf throw_jmp_buf; |
| |
\c |
| |
\c void *gforth_thread(user_area * t) |
| |
\c { |
| |
\c void *x; |
| |
\c int throw_code; |
| |
\c pthread_cleanup_push(&gforth_cleanup_thread, (void*)t); |
| |
\c |
| |
\c if ((throw_code=setjmp(throw_jmp_buf))) { |
| |
\c static Cell signal_data_stack[24]; |
| |
\c static Cell signal_return_stack[16]; |
| |
\c static Float signal_fp_stack[1]; |
| |
\c |
| |
\c signal_data_stack[15]=throw_code; |
| |
\c x=gforth_engine((Cell*)(t->throw_entry), signal_data_stack+15, |
| |
\c signal_return_stack+16, signal_fp_stack, 0); |
| |
\c } else { |
| |
\c ((Cell*)(t->sp0))[-1]=(Cell)t; |
| |
\c x=gforth_engine((void*)(t->save_task), (Cell*)(t->sp0)-1, (Cell*)(t->rp0), (Float*)(t->fp0), (void*)(t->lp0)); |
| |
\c } |
| |
\c pthread_cleanup_pop(1); |
| |
\c return x; |
| \c } |
\c } |
| \c void *gforth_thread_p() |
\c void *gforth_thread_p() |
| \c { |
\c { |
| c-function pthread+ pthread_plus a -- a ( addr -- addr' ) |
c-function pthread+ pthread_plus a -- a ( addr -- addr' ) |
| c-function pthreads pthreads n -- n ( n -- n' ) |
c-function pthreads pthreads n -- n ( n -- n' ) |
| c-function thread_start gforth_thread_p -- a ( -- addr ) |
c-function thread_start gforth_thread_p -- a ( -- addr ) |
| |
c-function gforth_create_thread gforth_create_thread n n n n -- a ( dsize rsize fsize lsize -- task ) |
| c-function pthread_create pthread_create a a a a -- n ( thread attr start arg ) |
c-function pthread_create pthread_create a a a a -- n ( thread attr start arg ) |
| c-function pthread_exit pthread_exit a -- void ( retaddr -- ) |
c-function pthread_exit pthread_exit a -- void ( retaddr -- ) |
| c-function pthread_mutex_init pthread_mutex_init a a -- n ( mutex addr -- r ) |
c-function pthread_mutex_init pthread_mutex_init a a -- n ( mutex addr -- r ) |
| c-function pause pthread_yield -- void ( -- ) |
c-function pause pthread_yield -- void ( -- ) |
| end-c-library |
end-c-library |
| |
|
| begin-structure threadId |
User pthread-id -1 cells pthread+ uallot drop |
| field: data_stack_size |
|
| field: fp_stack_size |
:noname ' >body @ ; |
| field: return_stack_size |
:noname ' >body @ postpone literal ; |
| field: locals_stack_size |
interpret/compile: user' ( 'user' -- n ) |
| field: t_sp0 |
\G USER' computes the task offset of a user variable |
| field: t_fp0 |
|
| field: t_rp0 |
: >task ( user task -- user' ) + next-task - ; |
| field: t_lp0 |
|
| field: boot_entry |
: kill-task ( -- ) |
| field: saved_ip |
0 (bye) ; |
| field: saved_rp |
|
| 1 pthreads +field t_pthread |
:noname ( -- ) |
| end-structure |
[ here throw-entry ! ] |
| |
handler @ ?dup-0=-IF |
| : new-thread ( xt -- id ) |
>stderr cr ." uncaught thread exception: " .error cr |
| threadId allocate throw >r |
kill-task |
| forthstart 3 cells + r@ 4 cells move |
THEN |
| >body r@ boot_entry ! |
(throw1) ; drop |
| r@ t_pthread 0 thread_start r@ pthread_create |
|
| drop \ fixme |
: NewTask4 ( dsize rsize fsize lsize -- task ) |
| |
gforth_create_thread >r |
| |
throw-entry r@ udp @ throw-entry next-task - /string move |
| |
word-pno-size chars dup allocate throw dup holdbufptr r@ >task ! |
| |
+ dup holdptr r@ >task ! holdend r@ >task ! |
| |
['] kill-task >body rp0 r@ >task @ 1 cells - dup rp0 r@ >task ! ! |
| |
handler r@ >task off |
| r> ; |
r> ; |
| |
|
| : semaphore ( "name" -- ) |
: NewTask ( stacksize -- task ) dup 2dup NewTask4 ; |
| |
|
| |
: (activate) ( task -- ) |
| |
r> swap >r save-task r@ >task ! |
| |
pthread-id r@ >task 0 thread_start r> pthread_create drop ; compile-only |
| |
|
| |
: activate ]] (activate) up! [[ ; immediate compile-only |
| |
|
| |
: (pass) ( x1 .. xn n task -- ) |
| |
r> swap >r save-task r@ >task ! |
| |
1+ dup cells negate sp0 r@ >task @ -rot sp0 r@ >task +! |
| |
sp0 r@ >task @ swap 0 ?DO tuck ! cell+ LOOP drop |
| |
pthread-id r@ >task 0 thread_start r> pthread_create drop ; compile-only |
| |
|
| |
: pass ]] (pass) up! sp0 ! [[ ; immediate compile-only |
| |
|
| |
: sema ( "name" -- ) \ gforth |
| |
\G create a named semaphore |
| Create here 1 pthread-mutexes allot 0 pthread_mutex_init drop ; |
Create here 1 pthread-mutexes allot 0 pthread_mutex_init drop ; |
| |
|
| : lock ( addr -- ) pthread_mutex_lock drop ; |
: lock ( addr -- ) pthread_mutex_lock drop ; |
| : unlock ( addr -- ) pthread_mutex_unlock drop ; |
: unlock ( addr -- ) pthread_mutex_unlock drop ; |
| |
|
| |
: stacksize ( -- n ) forthstart 4 cells + @ ; |
| |
: stacksize4 ( -- dsize rsize fsize lsize ) |
| |
forthstart 4 cells + 4 cells bounds DO I @ cell +LOOP ; |
| |
|
| false [IF] \ test |
false [IF] \ test |
| semaphore testsem |
semaphore testsem |
| |
|
| : test-thread1 |
: test-thread1 |
| |
stacksize NewTask activate 0 hex |
| BEGIN |
BEGIN |
| testsem lock |
testsem lock |
| ." Thread-Test1" cr 1000 ms |
." Thread-Test1 " dup . cr 1000 ms |
| testsem unlock |
testsem unlock 1+ |
| 100 0 DO pause LOOP |
100 0 DO pause LOOP |
| AGAIN ; |
AGAIN ; |
| |
|
| : test-thread2 |
: test-thread2 |
| |
stacksize NewTask activate 0 decimal |
| BEGIN |
BEGIN |
| testsem lock |
testsem lock |
| ." Thread-Test2" cr 1000 ms |
." Thread-Test2 " dup . cr 1000 ms |
| testsem unlock |
testsem unlock 1+ |
| 100 0 DO pause LOOP |
100 0 DO pause LOOP |
| AGAIN ; |
AGAIN ; |
| |
|
| ' test-thread1 new-thread Constant test-id1 |
test-thread1 |
| ' test-thread2 new-thread Constant test-id2 |
test-thread2 |
| [THEN] |
[THEN] |