Changeset b9641ee in mainline
- Timestamp:
- 2007-06-27T23:12:25Z (18 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- bc1f1c2
- Parents:
- 72381f1
- Location:
- uspace/lib/libc
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/libc/generic/psthread.c
r72381f1 rb9641ee 1 1 /* 2 2 * Copyright (c) 2006 Ondrej Palkovsky 3 * Copyright (c) 2007 Jakub Jermar 3 4 * All rights reserved. 4 5 * … … 45 46 46 47 #ifndef PSTHREAD_INITIAL_STACK_PAGES_NO 47 #define PSTHREAD_INITIAL_STACK_PAGES_NO 48 #define PSTHREAD_INITIAL_STACK_PAGES_NO 1 48 49 #endif 49 50 … … 56 57 static atomic_t psthread_futex = FUTEX_INITIALIZER; 57 58 /** Count of real threads that are in async_serialized mode */ 58 static int serialized_threads; 59 static int serialized_threads; /* Protected by async_futex */ 59 60 /** Thread-local count of serialization. If >0, we must not preempt */ 60 61 static __thread int serialization_count; … … 63 64 64 65 /** Setup psthread information into TCB structure */ 65 psthread_data_t * psthread_setup()66 psthread_data_t *psthread_setup(void) 66 67 { 67 68 psthread_data_t *pt; … … 90 91 } 91 92 92 /** Function that is called on entry to new pseudo thread */ 93 /** Function that spans the whole life-cycle of a pseudo thread. 94 * 95 * Each pseudo thread begins execution in this function. 96 * Then the function implementing the pseudo thread logic is called. 97 * After its return, the return value is saved for a potentional 98 * joiner. If the joiner exists, it is woken up. The pseudo thread 99 * then switches to another pseudo thread, which cleans up after it. 100 */ 93 101 void psthread_main(void) 94 102 { … … 97 105 pt->retval = pt->func(pt->arg); 98 106 99 pt->finished = 1; 100 if (pt->waiter) 101 list_append(&pt->waiter->link, &ready_list); 107 /* 108 * If there is a joiner, wake it up and save our return value. 109 */ 110 if (pt->joiner) { 111 list_append(&pt->joiner->link, &ready_list); 112 pt->joiner->joinee_retval = pt->retval; 113 } 102 114 103 115 psthread_schedule_next_adv(PS_FROM_DEAD); 116 /* not reached */ 104 117 } 105 118 … … 109 122 * held. 110 123 * 111 * @param ctype Type of switch. 112 * @return 0 if there is no ready pseudo thread, 1 otherwise. 124 * @param ctype One of PS_SLEEP, PS_PREEMPT, PS_TO_MANAGER, 125 * PS_FROM_MANAGER, PS_FROM_DEAD. The parameter describes 126 * the circumstances of the switch. 127 * @return Return 0 if there is no ready pseudo thread, 128 * return 1 otherwise. 113 129 */ 114 130 int psthread_schedule_next_adv(pschange_type ctype) … … 121 137 if (ctype == PS_PREEMPT && list_empty(&ready_list)) 122 138 goto ret_0; 139 if (ctype == PS_SLEEP) { 140 if (list_empty(&ready_list) && list_empty(&serialized_list)) 141 goto ret_0; 142 } 123 143 124 144 if (ctype == PS_FROM_MANAGER) { 125 145 if (list_empty(&ready_list) && list_empty(&serialized_list)) 126 146 goto ret_0; 127 /* Do not preempt if there is not sufficient count of thread managers */ 128 if (list_empty(&serialized_list) && threads_in_manager <= serialized_threads) { 147 /* 148 * Do not preempt if there is not sufficient count of thread 149 * managers. 150 */ 151 if (list_empty(&serialized_list) && threads_in_manager <= 152 serialized_threads) { 129 153 goto ret_0; 130 154 } … … 139 163 } 140 164 165 srcpt = __tcb_get()->pst_data; 141 166 if (ctype != PS_FROM_DEAD) { 142 167 /* Save current state */ 143 srcpt = __tcb_get()->pst_data;144 168 if (!context_save(&srcpt->ctx)) { 145 169 if (serialization_count) 146 170 srcpt->flags &= ~PSTHREAD_SERIALIZED; 147 return 1; // futex_up already done here 148 } 149 150 /* Save myself to correct run list */ 171 if (srcpt->clean_after_me) { 172 /* 173 * Cleanup after the dead pseudo thread from 174 * which we restored context here. 175 */ 176 free(srcpt->clean_after_me->stack); 177 psthread_teardown(srcpt->clean_after_me); 178 srcpt->clean_after_me = NULL; 179 } 180 return 1; /* futex_up already done here */ 181 } 182 183 /* Save myself to the correct run list */ 151 184 if (ctype == PS_PREEMPT) 152 185 list_append(&srcpt->link, &ready_list); … … 154 187 list_append(&srcpt->link, &manager_list); 155 188 threads_in_manager--; 156 } /* If ctype == PS_TO_MANAGER, don't save ourselves to any list, we should 157 * already be somewhere, or we will be lost */ 158 } else 159 srcpt = NULL; /* Avoid GCC warning, if ctype == PS_FROM_DEAD, srcpt is not used */ 189 } else { 190 /* 191 * If ctype == PS_TO_MANAGER, don't save ourselves to 192 * any list, we should already be somewhere, or we will 193 * be lost. 194 * 195 * The ctype == PS_SLEEP case is similar. The pseudo 196 * thread has an external refernce which can be used to 197 * wake it up once that time has come. 198 */ 199 } 200 } 160 201 161 202 /* Choose new thread to run */ 162 203 if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) { 163 dstpt = list_get_instance(manager_list.next,psthread_data_t, link); 204 dstpt = list_get_instance(manager_list.next, psthread_data_t, 205 link); 164 206 if (serialization_count && ctype == PS_TO_MANAGER) { 165 207 serialized_threads++; … … 167 209 } 168 210 threads_in_manager++; 211 212 if (ctype == PS_FROM_DEAD) 213 dstpt->clean_after_me = srcpt; 169 214 } else { 170 215 if (!list_empty(&serialized_list)) { 171 dstpt = list_get_instance(serialized_list.next, psthread_data_t, link); 216 dstpt = list_get_instance(serialized_list.next, 217 psthread_data_t, link); 172 218 serialized_threads--; 173 } else 174 dstpt = list_get_instance(ready_list.next, psthread_data_t, link); 219 } else { 220 dstpt = list_get_instance(ready_list.next, 221 psthread_data_t, link); 222 } 175 223 } 176 224 list_remove(&dstpt->link); … … 178 226 futex_up(&psthread_futex); 179 227 context_restore(&dstpt->ctx); 228 /* not reached */ 180 229 181 230 ret_0: … … 186 235 /** Wait for uspace pseudo thread to finish. 187 236 * 188 * @param psthrid Pseudo thread to wait for. 189 * 190 * @return Value returned by the finished thread. 237 * Each pseudo thread can be only joined by one other pseudo thread. Moreover, 238 * the joiner must be from the same thread as the joinee. 239 * 240 * @param psthrid Pseudo thread to join. 241 * 242 * @return Value returned by the finished thread. 191 243 */ 192 244 int psthread_join(pstid_t psthrid) 193 245 { 194 volatilepsthread_data_t *pt;195 volatile int retval;246 psthread_data_t *pt; 247 psthread_data_t *cur; 196 248 197 249 /* Handle psthrid = Kernel address -> it is wait for call */ 198 250 pt = (psthread_data_t *) psthrid; 199 251 200 /* TODO */ 201 printf("join unsupported\n"); 202 _exit(1); 203 204 retval = pt->retval; 205 206 free(pt->stack); 207 psthread_teardown((void *)pt); 208 209 return retval; 252 /* 253 * The joiner is running so the joinee isn't. 254 */ 255 cur = __tcb_get()->pst_data; 256 pt->joiner = cur; 257 psthread_schedule_next_adv(PS_SLEEP); 258 259 /* 260 * The joinee fills in the return value. 261 */ 262 return cur->joinee_retval; 210 263 } 211 264 212 265 /** Create a userspace pseudo thread. 213 266 * 214 * @param func 215 * @param arg 216 * 217 * @return 0 on failure,TLS of the new pseudo thread.267 * @param func Pseudo thread function. 268 * @param arg Argument to pass to func. 269 * 270 * @return Return 0 on failure or TLS of the new pseudo thread. 218 271 */ 219 272 pstid_t psthread_create(int (*func)(void *), void *arg) … … 224 277 if (!pt) 225 278 return 0; 226 pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize()); 279 pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO * 280 getpagesize()); 227 281 228 282 if (!pt->stack) { … … 233 287 pt->arg= arg; 234 288 pt->func = func; 235 pt->finished = 0; 236 pt->waiter = NULL; 289 pt->clean_after_me = NULL; 290 pt->joiner = NULL; 291 pt->joinee_retval = 0; 292 pt->retval = 0; 237 293 pt->flags = 0; 238 294 239 295 context_save(&pt->ctx); 240 context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(), pt->tcb); 241 242 return (pstid_t )pt; 243 } 244 245 /** Add a thread to ready list */ 296 context_set(&pt->ctx, FADDR(psthread_main), pt->stack, 297 PSTHREAD_INITIAL_STACK_PAGES_NO * getpagesize(), pt->tcb); 298 299 return (pstid_t) pt; 300 } 301 302 /** Add a thread to the ready list. 303 * 304 * @param psthrid Pinter to the pseudo thread structure of the 305 * pseudo thread to be added. 306 */ 246 307 void psthread_add_ready(pstid_t psthrid) 247 308 { … … 257 318 } 258 319 259 /** Add a thread to manager list */ 320 /** Add a pseudo thread to the manager list. 321 * 322 * @param psthrid Pinter to the pseudo thread structure of the 323 * pseudo thread to be added. 324 */ 260 325 void psthread_add_manager(pstid_t psthrid) 261 326 { … … 270 335 271 336 /** Remove one manager from manager list */ 272 void psthread_remove_manager( )337 void psthread_remove_manager(void) 273 338 { 274 339 futex_down(&psthread_futex); … … 281 346 } 282 347 283 /** Return thread id of current running thread */ 348 /** Return thread id of the currently running thread. 349 * 350 * @return Pseudo thread ID of the currently running pseudo thread. 351 */ 284 352 pstid_t psthread_get_id(void) 285 353 { 286 return (pstid_t) __tcb_get()->pst_data;354 return (pstid_t) __tcb_get()->pst_data; 287 355 } 288 356 289 357 /** Disable preemption 290 358 * 291 * If the thread wants to send several message in row and does not want292 * to be preempted, it should start async_serialize_start() in the beginning293 * of communication and async_serialize_end() in the end. If it is a294 * true multithreaded application, it should protect the communication channel295 * by a futex as well. Interrupt messages willcan still be preempted.359 * If the thread wants to send several message in a row and does not want to be 360 * preempted, it should start async_serialize_start() in the beginning of 361 * communication and async_serialize_end() in the end. If it is a true 362 * multithreaded application, it should protect the communication channel by a 363 * futex as well. Interrupt messages can still be preempted. 296 364 */ 297 365 void psthread_inc_sercount(void) … … 300 368 } 301 369 370 /** Restore the preemption counter to the previous state. */ 302 371 void psthread_dec_sercount(void) 303 372 { … … 307 376 /** @} 308 377 */ 378 -
uspace/lib/libc/include/psthread.h
r72381f1 rb9641ee 50 50 51 51 typedef enum { 52 PS_SLEEP, 53 PS_PREEMPT, 52 54 PS_TO_MANAGER, 53 55 PS_FROM_MANAGER, 54 PS_PREEMPT,55 56 PS_FROM_DEAD 56 57 } pschange_type; … … 66 67 tcb_t *tcb; 67 68 68 struct psthread_data *waiter; 69 int finished; 69 struct psthread_data *clean_after_me; 70 struct psthread_data *joiner; 71 int joinee_retval; 70 72 int retval; 71 73 int flags; … … 88 90 void psthread_dec_sercount(void); 89 91 90 static inline int psthread_schedule_next( ) {92 static inline int psthread_schedule_next(void) { 91 93 return psthread_schedule_next_adv(PS_PREEMPT); 92 94 } 93 94 95 95 96 #endif
Note:
See TracChangeset
for help on using the changeset viewer.