annotate libhsail-rt/rt/fibers.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 /* fibers.c -- extremely simple lightweight thread (fiber) implementation
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
2 Copyright (C) 2016-2020 Free Software Foundation, Inc.
111
kono
parents:
diff changeset
3 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
kono
parents:
diff changeset
4 for General Processor Tech.
kono
parents:
diff changeset
5
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
6 Copyright (C) 2015-2020 Free Software Foundation, Inc.
111
kono
parents:
diff changeset
7 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
kono
parents:
diff changeset
8 for General Processor Tech.
kono
parents:
diff changeset
9
kono
parents:
diff changeset
10 Permission is hereby granted, free of charge, to any person obtaining a
kono
parents:
diff changeset
11 copy of this software and associated documentation files
kono
parents:
diff changeset
12 (the "Software"), to deal in the Software without restriction, including
kono
parents:
diff changeset
13 without limitation the rights to use, copy, modify, merge, publish,
kono
parents:
diff changeset
14 distribute, sublicense, and/or sell copies of the Software, and to
kono
parents:
diff changeset
15 permit persons to whom the Software is furnished to do so, subject to
kono
parents:
diff changeset
16 the following conditions:
kono
parents:
diff changeset
17
kono
parents:
diff changeset
18 The above copyright notice and this permission notice shall be included
kono
parents:
diff changeset
19 in all copies or substantial portions of the Software.
kono
parents:
diff changeset
20
kono
parents:
diff changeset
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
kono
parents:
diff changeset
22 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
kono
parents:
diff changeset
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
kono
parents:
diff changeset
24 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
kono
parents:
diff changeset
25 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
kono
parents:
diff changeset
26 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
kono
parents:
diff changeset
27 USE OR OTHER DEALINGS IN THE SOFTWARE.
kono
parents:
diff changeset
28 */
kono
parents:
diff changeset
29
kono
parents:
diff changeset
30 #include <stdlib.h>
kono
parents:
diff changeset
31 #include <stdio.h>
kono
parents:
diff changeset
32 #include <stdint.h>
kono
parents:
diff changeset
33
kono
parents:
diff changeset
34 #include "target-config.h"
kono
parents:
diff changeset
35
kono
parents:
diff changeset
36 #include "fibers.h"
kono
parents:
diff changeset
37
kono
parents:
diff changeset
38 void
kono
parents:
diff changeset
39 phsa_fatal_error (int code);
kono
parents:
diff changeset
40
kono
parents:
diff changeset
41 ucontext_t main_context;
kono
parents:
diff changeset
42
kono
parents:
diff changeset
43 /* The last fiber in the linked list. */
kono
parents:
diff changeset
44 static fiber_t *tail_fiber = NULL;
kono
parents:
diff changeset
45 /* The first fiber in the linked list. */
kono
parents:
diff changeset
46 static fiber_t *head_fiber = NULL;
kono
parents:
diff changeset
47 /* The fiber currently being executed. */
kono
parents:
diff changeset
48 static fiber_t *current_fiber = NULL;
kono
parents:
diff changeset
49
kono
parents:
diff changeset
50 /* Makecontext accepts only integer arguments. We need to split the
kono
parents:
diff changeset
51 pointer argument in case pointer does not fit into int. This helper
kono
parents:
diff changeset
52 function can be used to restore the pointer from the arguments. */
kono
parents:
diff changeset
53
kono
parents:
diff changeset
54 void *
kono
parents:
diff changeset
55 fiber_int_args_to_ptr (int arg0, int arg1)
kono
parents:
diff changeset
56 {
kono
parents:
diff changeset
57 void *ptr = NULL;
kono
parents:
diff changeset
58 #if SIZEOF_VOIDP == 8 && SIZEOF_INT == 4
kono
parents:
diff changeset
59 ptr = (void*)(((uint64_t) arg0 & (uint64_t) 0xFFFFFFFF)
kono
parents:
diff changeset
60 | ((uint64_t) arg1 << 32));
kono
parents:
diff changeset
61 #elif SIZEOF_VOIDP == 4 && SIZEOF_INT == 4
kono
parents:
diff changeset
62 ptr = (void*)arg0;
kono
parents:
diff changeset
63 #else
kono
parents:
diff changeset
64 # error Unsupported pointer/int size.
kono
parents:
diff changeset
65 #endif
kono
parents:
diff changeset
66 return ptr;
kono
parents:
diff changeset
67 }
kono
parents:
diff changeset
68
kono
parents:
diff changeset
69 void
kono
parents:
diff changeset
70 fiber_init (fiber_t *fiber, fiber_function_t start_function, void *arg,
kono
parents:
diff changeset
71 size_t stack_size, size_t stack_align)
kono
parents:
diff changeset
72 {
kono
parents:
diff changeset
73 int arg0, arg1;
kono
parents:
diff changeset
74 if (getcontext (&fiber->context) != 0)
kono
parents:
diff changeset
75 phsa_fatal_error (3);
kono
parents:
diff changeset
76 if (posix_memalign (&fiber->context.uc_stack.ss_sp, stack_align, stack_size)
kono
parents:
diff changeset
77 != 0)
kono
parents:
diff changeset
78 phsa_fatal_error (4);
kono
parents:
diff changeset
79 fiber->context.uc_stack.ss_size = stack_size;
kono
parents:
diff changeset
80 fiber->context.uc_link = &main_context;
kono
parents:
diff changeset
81
kono
parents:
diff changeset
82 /* makecontext () accepts only integer arguments. Split the
kono
parents:
diff changeset
83 pointer argument to two args in the case pointer does not fit
kono
parents:
diff changeset
84 into one int. */
kono
parents:
diff changeset
85 #if SIZEOF_VOIDP == 8 && SIZEOF_INT == 4
kono
parents:
diff changeset
86 arg0 = (int32_t) 0xFFFFFFFF & (uint64_t)arg;
kono
parents:
diff changeset
87 arg1 = (int32_t) 0xFFFFFFFF & ((uint64_t)arg >> 32);
kono
parents:
diff changeset
88 #elif SIZEOF_VOIDP == 4 && SIZEOF_INT == 4
kono
parents:
diff changeset
89 arg0 = (int)arg;
kono
parents:
diff changeset
90 arg1 = 0;
kono
parents:
diff changeset
91 #else
kono
parents:
diff changeset
92 # error Unsupported pointer/int size.
kono
parents:
diff changeset
93 #endif
kono
parents:
diff changeset
94
kono
parents:
diff changeset
95 makecontext (&fiber->context, (void*)start_function, 2, arg0, arg1);
kono
parents:
diff changeset
96
kono
parents:
diff changeset
97 fiber->status = FIBER_STATUS_READY;
kono
parents:
diff changeset
98 fiber->next = NULL;
kono
parents:
diff changeset
99 fiber->prev = NULL;
kono
parents:
diff changeset
100
kono
parents:
diff changeset
101 /* Create a linked list of the created fibers. Append the new one at
kono
parents:
diff changeset
102 the end. */
kono
parents:
diff changeset
103 if (tail_fiber == NULL)
kono
parents:
diff changeset
104 tail_fiber = fiber;
kono
parents:
diff changeset
105 else
kono
parents:
diff changeset
106 {
kono
parents:
diff changeset
107 tail_fiber->next = fiber;
kono
parents:
diff changeset
108 fiber->prev = tail_fiber;
kono
parents:
diff changeset
109 tail_fiber = fiber;
kono
parents:
diff changeset
110 }
kono
parents:
diff changeset
111
kono
parents:
diff changeset
112 if (head_fiber == NULL)
kono
parents:
diff changeset
113 head_fiber = fiber;
kono
parents:
diff changeset
114 }
kono
parents:
diff changeset
115
kono
parents:
diff changeset
116 void
kono
parents:
diff changeset
117 fiber_exit ()
kono
parents:
diff changeset
118 {
kono
parents:
diff changeset
119 fiber_status_t old_status = current_fiber->status;
kono
parents:
diff changeset
120 current_fiber->status = FIBER_STATUS_EXITED;
kono
parents:
diff changeset
121 if (old_status == FIBER_STATUS_JOINED)
kono
parents:
diff changeset
122 /* In case this thread has been joined, return back to the joiner. */
kono
parents:
diff changeset
123 swapcontext (&current_fiber->context, &main_context);
kono
parents:
diff changeset
124 else
kono
parents:
diff changeset
125 /* In case the thread exited while being yielded from another thread,
kono
parents:
diff changeset
126 switch back to another fiber. */
kono
parents:
diff changeset
127 fiber_yield ();
kono
parents:
diff changeset
128 }
kono
parents:
diff changeset
129
kono
parents:
diff changeset
130 void
kono
parents:
diff changeset
131 fiber_join (fiber_t *fiber)
kono
parents:
diff changeset
132 {
kono
parents:
diff changeset
133 fiber_t *next_ready_fiber = NULL;
kono
parents:
diff changeset
134 current_fiber = fiber;
kono
parents:
diff changeset
135 if (fiber->status != FIBER_STATUS_EXITED)
kono
parents:
diff changeset
136 {
kono
parents:
diff changeset
137 fiber->status = FIBER_STATUS_JOINED;
kono
parents:
diff changeset
138 while (fiber->status != FIBER_STATUS_EXITED)
kono
parents:
diff changeset
139 swapcontext (&main_context, &fiber->context);
kono
parents:
diff changeset
140 }
kono
parents:
diff changeset
141
kono
parents:
diff changeset
142 /* Remove the successfully joined fiber from the linked list so we won't
kono
parents:
diff changeset
143 access it later (the fiber itself might be freed after the join). */
kono
parents:
diff changeset
144 if (fiber->prev != NULL)
kono
parents:
diff changeset
145 fiber->prev->next = fiber->next;
kono
parents:
diff changeset
146
kono
parents:
diff changeset
147 if (fiber->next != NULL)
kono
parents:
diff changeset
148 fiber->next->prev = fiber->prev;
kono
parents:
diff changeset
149
kono
parents:
diff changeset
150 if (head_fiber == fiber)
kono
parents:
diff changeset
151 head_fiber = fiber->next;
kono
parents:
diff changeset
152
kono
parents:
diff changeset
153 if (tail_fiber == fiber)
kono
parents:
diff changeset
154 tail_fiber = fiber->prev;
kono
parents:
diff changeset
155
kono
parents:
diff changeset
156 free (fiber->context.uc_stack.ss_sp);
kono
parents:
diff changeset
157 }
kono
parents:
diff changeset
158
kono
parents:
diff changeset
159 void
kono
parents:
diff changeset
160 fiber_yield ()
kono
parents:
diff changeset
161 {
kono
parents:
diff changeset
162 fiber_t *next_ready_fiber = current_fiber;
kono
parents:
diff changeset
163
kono
parents:
diff changeset
164 if (current_fiber == head_fiber
kono
parents:
diff changeset
165 && current_fiber == tail_fiber)
kono
parents:
diff changeset
166 {
kono
parents:
diff changeset
167 /* If the last fiber exits independently, there is no
kono
parents:
diff changeset
168 fiber to switch to. Switch to the main context in that
kono
parents:
diff changeset
169 case. */
kono
parents:
diff changeset
170 if (current_fiber->status == FIBER_STATUS_EXITED)
kono
parents:
diff changeset
171 swapcontext (&current_fiber->context, &main_context);
kono
parents:
diff changeset
172 }
kono
parents:
diff changeset
173
kono
parents:
diff changeset
174 do {
kono
parents:
diff changeset
175 next_ready_fiber = next_ready_fiber->next != NULL
kono
parents:
diff changeset
176 ? next_ready_fiber->next : head_fiber;
kono
parents:
diff changeset
177 } while (next_ready_fiber != current_fiber
kono
parents:
diff changeset
178 && next_ready_fiber->status == FIBER_STATUS_EXITED);
kono
parents:
diff changeset
179
kono
parents:
diff changeset
180 fiber_t *old_current_fiber = current_fiber;
kono
parents:
diff changeset
181 current_fiber = next_ready_fiber;
kono
parents:
diff changeset
182 swapcontext (&old_current_fiber->context, &next_ready_fiber->context);
kono
parents:
diff changeset
183 }
kono
parents:
diff changeset
184
kono
parents:
diff changeset
185 size_t
kono
parents:
diff changeset
186 fiber_barrier_reach (fiber_barrier_t *barrier)
kono
parents:
diff changeset
187 {
kono
parents:
diff changeset
188 /* Yield once to ensure that there are no fibers waiting for
kono
parents:
diff changeset
189 a previous triggering of the barrier in the waiting_count
kono
parents:
diff changeset
190 loop. This should release them before we update the reached
kono
parents:
diff changeset
191 counter again. */
kono
parents:
diff changeset
192 fiber_yield ();
kono
parents:
diff changeset
193
kono
parents:
diff changeset
194 barrier->reached++;
kono
parents:
diff changeset
195 ++barrier->waiting_count;
kono
parents:
diff changeset
196 while (barrier->reached < barrier->threshold)
kono
parents:
diff changeset
197 fiber_yield ();
kono
parents:
diff changeset
198 --barrier->waiting_count;
kono
parents:
diff changeset
199
kono
parents:
diff changeset
200 /* Wait until all the fibers have reached this point. */
kono
parents:
diff changeset
201 while (barrier->waiting_count > 0)
kono
parents:
diff changeset
202 fiber_yield ();
kono
parents:
diff changeset
203
kono
parents:
diff changeset
204 /* Now all fibers have been released from the barrier waiting
kono
parents:
diff changeset
205 loop. We can now safely reset the reach count for new triggering. */
kono
parents:
diff changeset
206 if (barrier->reached > 0)
kono
parents:
diff changeset
207 {
kono
parents:
diff changeset
208 barrier->reached = 0;
kono
parents:
diff changeset
209 return 0;
kono
parents:
diff changeset
210 }
kono
parents:
diff changeset
211 return 1;
kono
parents:
diff changeset
212 }
kono
parents:
diff changeset
213
kono
parents:
diff changeset
214 void
kono
parents:
diff changeset
215 fiber_barrier_init (fiber_barrier_t *barrier, size_t threshold)
kono
parents:
diff changeset
216 {
kono
parents:
diff changeset
217 barrier->threshold = threshold;
kono
parents:
diff changeset
218 barrier->waiting_count = 0;
kono
parents:
diff changeset
219 barrier->reached = 0;
kono
parents:
diff changeset
220 }