-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththreaded.c
More file actions
131 lines (101 loc) · 3.54 KB
/
threaded.c
File metadata and controls
131 lines (101 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
** This is an example of how to use MY-BASIC with multiple threads.
**
** I have disabled MB_ENABLE_UNICODE and MB_ENABLE_UNICODE_ID, otherwise calling
** "setlocale" in "_print_string" may cause deadlocks.
**
** For info about the interpreter itself, see https://github.com/paladin-t/my_basic/
*/
#define THREAD_IMPLEMENTATION
#ifndef MB_CP_VC
# include <stdint.h>
#endif /* MB_CP_VC */
#include <stdio.h>
#include <string.h>
#include "core/my_basic.h"
#include "libs/thread.h"
#define THREAD_COUNT 10
// Delay for a certain period.
static int delay(struct mb_interpreter_t* s, void** l) {
int result = MB_FUNC_OK;
int_t ms = 0;
thread_timer_t tmr;
mb_check(mb_attempt_func_begin(s, l));
mb_check(mb_pop_int(s, l, &ms));
mb_check(mb_attempt_func_end(s, l));
thread_timer_init(&tmr);
thread_timer_wait(&tmr, (THREAD_U64)ms * 1000000);
thread_timer_term(&tmr);
return result;
}
// Print something.
static int talk(struct mb_interpreter_t* s, void** l) {
int result = MB_FUNC_OK;
void* data = NULL;
mb_check(mb_attempt_func_begin(s, l));
mb_check(mb_attempt_func_end(s, l));
mb_get_userdata(s, &data);
printf("THREAD %d\n", (int)(intptr_t)data);
return result;
}
// Thread procedure for fully created interpreter instance.
static int threaded_full(void* data) {
struct mb_interpreter_t* bas = NULL;
int n = (int)(intptr_t)data;
char code[32] = { '\0' };
sprintf(code, "delay %d\ntalk", n * 1000);
// A whole open/reg/load/run/close workflow. Each instance has its own context
// as an isolated one.
mb_open(&bas);
mb_set_userdata(bas, data);
mb_reg_fun(bas, delay);
mb_reg_fun(bas, talk);
mb_load_string(bas, code, true);
mb_run(bas, true);
mb_close(&bas);
return 0;
}
// Thread procedure for forked interpreter instance.
static int threaded_fork(void* data) {
struct mb_interpreter_t* bas = NULL;
struct mb_interpreter_t* src = NULL;
src = (struct mb_interpreter_t*)data;
// A forked workflow. Shares the same registered functions, parsed code, etc.
// but uses its own running context.
// It's not fully supported to run forked instances with multiple threads,
// cannot use referenced GC types in code, although simple data types are OK.
// If you are not sure about it, just don't use this way.
mb_fork(&bas, src);
// IMPORTANT: pass "false" to "clear_parser" to avoid different threads
// writing to same memory, it will be cleared when calling "mb_close" later.
mb_run(bas, false);
mb_join(&bas);
return 0;
}
int main(int argc, char* argv[]) {
struct mb_interpreter_t* bas = NULL;
thread_ptr_t threads[THREAD_COUNT];
int i = 0;
// Call this only once per progress.
mb_init();
// Prepare an interpreter instance for further forking.
mb_open(&bas);
mb_set_userdata(bas, (void*)(intptr_t)-1);
mb_reg_fun(bas, delay);
mb_reg_fun(bas, talk);
mb_load_string(bas, "delay 100\nprint \"FORKED\"+chr(10)", true);
// Fork from a preloaded interpreter instance.
for (i = THREAD_COUNT / 2; i < THREAD_COUNT; ++i)
threads[i] = thread_create(threaded_fork, (void*)bas, "THREADED FORK", 0);
// Create new interpreter instances.
for (i = 0; i < THREAD_COUNT / 2; ++i)
threads[i] = thread_create(threaded_full, (void*)(intptr_t)i, "THREADED FULL", 0);
// Wait until all threads terminated.
for (i = 0; i < THREAD_COUNT; ++i)
thread_join(threads[i]);
// Close the shared interpreter instance after all forked ones are joined.
mb_close(&bas);
// Call this only once per progress.
mb_dispose();
return 0;
}