1 /* fibers.c -- extremely simple lightweight thread (fiber) implementation
2 Copyright (C) 2016-2017 Free Software Foundation, Inc.
3 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
4 for General Processor Tech.
6 Copyright (C) 2015-2017 Free Software Foundation, Inc.
7 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
8 for General Processor Tech.
10 Permission is hereby granted, free of charge, to any person obtaining a
11 copy of this software and associated documentation files
12 (the "Software"), to deal in the Software without restriction, including
13 without limitation the rights to use, copy, modify, merge, publish,
14 distribute, sublicense, and/or sell copies of the Software, and to
15 permit persons to whom the Software is furnished to do so, subject to
16 the following conditions:
18 The above copyright notice and this permission notice shall be included
19 in all copies or substantial portions of the Software.
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
25 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
26 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
27 USE OR OTHER DEALINGS IN THE SOFTWARE.
34 #include "target-config.h"
39 phsa_fatal_error (int code
);
41 ucontext_t main_context
;
43 /* The last fiber in the linked list. */
44 static fiber_t
*tail_fiber
= NULL
;
45 /* The first fiber in the linked list. */
46 static fiber_t
*head_fiber
= NULL
;
47 /* The fiber currently being executed. */
48 static fiber_t
*current_fiber
= NULL
;
50 /* Makecontext accepts only integer arguments. We need to split the
51 pointer argument in case pointer does not fit into int. This helper
52 function can be used to restore the pointer from the arguments. */
55 fiber_int_args_to_ptr (int arg0
, int arg1
)
58 #if SIZEOF_VOIDP == 8 && SIZEOF_INT == 4
59 ptr
= (void*)(((uint64_t) arg0
& (uint64_t) 0xFFFFFFFF)
60 | ((uint64_t) arg1
<< 32));
61 #elif SIZEOF_VOIDP == 4 && SIZEOF_INT == 4
64 # error Unsupported pointer/int size.
70 fiber_init (fiber_t
*fiber
, fiber_function_t start_function
, void *arg
,
71 size_t stack_size
, size_t stack_align
)
74 if (getcontext (&fiber
->context
) != 0)
76 if (posix_memalign (&fiber
->context
.uc_stack
.ss_sp
, stack_align
, stack_size
)
79 fiber
->context
.uc_stack
.ss_size
= stack_size
;
80 fiber
->context
.uc_link
= &main_context
;
82 /* makecontext () accepts only integer arguments. Split the
83 pointer argument to two args in the case pointer does not fit
85 #if SIZEOF_VOIDP == 8 && SIZEOF_INT == 4
86 arg0
= (int32_t) 0xFFFFFFFF & (uint64_t)arg
;
87 arg1
= (int32_t) 0xFFFFFFFF & ((uint64_t)arg
>> 32);
88 #elif SIZEOF_VOIDP == 4 && SIZEOF_INT == 4
92 # error Unsupported pointer/int size.
95 makecontext (&fiber
->context
, (void*)start_function
, 2, arg0
, arg1
);
97 fiber
->status
= FIBER_STATUS_READY
;
101 /* Create a linked list of the created fibers. Append the new one at
103 if (tail_fiber
== NULL
)
107 tail_fiber
->next
= fiber
;
108 fiber
->prev
= tail_fiber
;
112 if (head_fiber
== NULL
)
119 fiber_status_t old_status
= current_fiber
->status
;
120 current_fiber
->status
= FIBER_STATUS_EXITED
;
121 if (old_status
== FIBER_STATUS_JOINED
)
122 /* In case this thread has been joined, return back to the joiner. */
123 swapcontext (¤t_fiber
->context
, &main_context
);
125 /* In case the thread exited while being yielded from another thread,
126 switch back to another fiber. */
131 fiber_join (fiber_t
*fiber
)
133 fiber_t
*next_ready_fiber
= NULL
;
134 current_fiber
= fiber
;
135 if (fiber
->status
!= FIBER_STATUS_EXITED
)
137 fiber
->status
= FIBER_STATUS_JOINED
;
138 while (fiber
->status
!= FIBER_STATUS_EXITED
)
139 swapcontext (&main_context
, &fiber
->context
);
142 /* Remove the successfully joined fiber from the linked list so we won't
143 access it later (the fiber itself might be freed after the join). */
144 if (fiber
->prev
!= NULL
)
145 fiber
->prev
->next
= fiber
->next
;
147 if (fiber
->next
!= NULL
)
148 fiber
->next
->prev
= fiber
->prev
;
150 if (head_fiber
== fiber
)
151 head_fiber
= fiber
->next
;
153 if (tail_fiber
== fiber
)
154 tail_fiber
= fiber
->prev
;
156 free (fiber
->context
.uc_stack
.ss_sp
);
162 fiber_t
*next_ready_fiber
= current_fiber
;
164 if (current_fiber
== head_fiber
165 && current_fiber
== tail_fiber
)
167 /* If the last fiber exits independently, there is no
168 fiber to switch to. Switch to the main context in that
170 if (current_fiber
->status
== FIBER_STATUS_EXITED
)
171 swapcontext (¤t_fiber
->context
, &main_context
);
175 next_ready_fiber
= next_ready_fiber
->next
!= NULL
176 ? next_ready_fiber
->next
: head_fiber
;
177 } while (next_ready_fiber
!= current_fiber
178 && next_ready_fiber
->status
== FIBER_STATUS_EXITED
);
180 fiber_t
*old_current_fiber
= current_fiber
;
181 current_fiber
= next_ready_fiber
;
182 swapcontext (&old_current_fiber
->context
, &next_ready_fiber
->context
);
186 fiber_barrier_reach (fiber_barrier_t
*barrier
)
188 /* Yield once to ensure that there are no fibers waiting for
189 a previous triggering of the barrier in the waiting_count
190 loop. This should release them before we update the reached
195 ++barrier
->waiting_count
;
196 while (barrier
->reached
< barrier
->threshold
)
198 --barrier
->waiting_count
;
200 /* Wait until all the fibers have reached this point. */
201 while (barrier
->waiting_count
> 0)
204 /* Now all fibers have been released from the barrier waiting
205 loop. We can now safely reset the reach count for new triggering. */
206 if (barrier
->reached
> 0)
208 barrier
->reached
= 0;
215 fiber_barrier_init (fiber_barrier_t
*barrier
, size_t threshold
)
217 barrier
->threshold
= threshold
;
218 barrier
->waiting_count
= 0;
219 barrier
->reached
= 0;