compiler: make non-escaping Bound_method_expression not heap allocate
[official-gcc.git] / libhsail-rt / rt / fibers.c
blob96ae3052522e747ddf52dc570bd59c0a65b2b636
1 /* fibers.c -- extremely simple lightweight thread (fiber) implementation
2 Copyright (C) 2016-2018 Free Software Foundation, Inc.
3 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
4 for General Processor Tech.
6 Copyright (C) 2015-2018 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.
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <stdint.h>
34 #include "target-config.h"
36 #include "fibers.h"
38 void
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. */
54 void *
55 fiber_int_args_to_ptr (int arg0, int arg1)
57 void *ptr = NULL;
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
62 ptr = (void*)arg0;
63 #else
64 # error Unsupported pointer/int size.
65 #endif
66 return ptr;
69 void
70 fiber_init (fiber_t *fiber, fiber_function_t start_function, void *arg,
71 size_t stack_size, size_t stack_align)
73 int arg0, arg1;
74 if (getcontext (&fiber->context) != 0)
75 phsa_fatal_error (3);
76 if (posix_memalign (&fiber->context.uc_stack.ss_sp, stack_align, stack_size)
77 != 0)
78 phsa_fatal_error (4);
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
84 into one int. */
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
89 arg0 = (int)arg;
90 arg1 = 0;
91 #else
92 # error Unsupported pointer/int size.
93 #endif
95 makecontext (&fiber->context, (void*)start_function, 2, arg0, arg1);
97 fiber->status = FIBER_STATUS_READY;
98 fiber->next = NULL;
99 fiber->prev = NULL;
101 /* Create a linked list of the created fibers. Append the new one at
102 the end. */
103 if (tail_fiber == NULL)
104 tail_fiber = fiber;
105 else
107 tail_fiber->next = fiber;
108 fiber->prev = tail_fiber;
109 tail_fiber = fiber;
112 if (head_fiber == NULL)
113 head_fiber = fiber;
116 void
117 fiber_exit ()
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 (&current_fiber->context, &main_context);
124 else
125 /* In case the thread exited while being yielded from another thread,
126 switch back to another fiber. */
127 fiber_yield ();
130 void
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);
159 void
160 fiber_yield ()
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
169 case. */
170 if (current_fiber->status == FIBER_STATUS_EXITED)
171 swapcontext (&current_fiber->context, &main_context);
174 do {
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);
185 size_t
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
191 counter again. */
192 fiber_yield ();
194 barrier->reached++;
195 ++barrier->waiting_count;
196 while (barrier->reached < barrier->threshold)
197 fiber_yield ();
198 --barrier->waiting_count;
200 /* Wait until all the fibers have reached this point. */
201 while (barrier->waiting_count > 0)
202 fiber_yield ();
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;
209 return 0;
211 return 1;
214 void
215 fiber_barrier_init (fiber_barrier_t *barrier, size_t threshold)
217 barrier->threshold = threshold;
218 barrier->waiting_count = 0;
219 barrier->reached = 0;