unsigned int create_thread(void (*function)(void), void* stack, size_t stack_size, unsigned flags, const char *name) { struct thread_entry *thread; SDL_Thread* t; SDL_sem *s; THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : ""); thread = find_empty_thread_slot(); if (thread == NULL) { DEBUGF("Failed to find thread slot\n"); return 0; } s = SDL_CreateSemaphore(0); if (s == NULL) { DEBUGF("Failed to create semaphore\n"); return 0; } t = SDL_CreateThread(runthread, thread); if (t == NULL) { DEBUGF("Failed to create SDL thread\n"); SDL_DestroySemaphore(s); return 0; } thread->stack = stack; thread->stack_size = stack_size; thread->name = name; thread->state = (flags & CREATE_THREAD_FROZEN) ? STATE_FROZEN : STATE_RUNNING; thread->context.start = function; thread->context.t = t; thread->context.s = s; THREAD_SDL_DEBUGF("New Thread: %d (%s)\n", thread - threads, THREAD_SDL_GET_NAME(thread)); return thread->id; }
void remove_thread(unsigned int thread_id) #endif { struct thread_entry *current = cores[CURRENT_CORE].running; struct thread_entry *thread = thread_id_entry(thread_id); SDL_Thread *t; SDL_sem *s; if (thread_id != THREAD_ID_CURRENT && thread->id != thread_id) return; int oldlevel = disable_irq_save(); t = thread->context.t; s = thread->context.s; thread->context.t = NULL; if (thread != current) { switch (thread->state) { case STATE_BLOCKED: case STATE_BLOCKED_W_TMO: /* Remove thread from object it's waiting on */ remove_from_list_l(thread->bqp, thread); #ifdef HAVE_WAKEUP_EXT_CB if (thread->wakeup_ext_cb != NULL) thread->wakeup_ext_cb(thread); #endif break; } SDL_SemPost(s); } THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n", thread - threads, THREAD_SDL_GET_NAME(thread)); new_thread_id(thread->id, thread); thread->state = STATE_KILLED; thread_queue_wake(&thread->queue); SDL_DestroySemaphore(s); if (thread == current) { /* Do a graceful exit - perform the longjmp back into the thread function to return */ restore_irq(oldlevel); longjmp(thread_jmpbufs[current - threads], 1); } SDL_KillThread(t); restore_irq(oldlevel); }
void init_threads(void) { /* Main thread is already initialized */ if (cores[CURRENT_CORE].running != &threads[0]) { THREAD_PANICF("Wrong main thread in init_threads: %p\n", cores[CURRENT_CORE].running); } THREAD_SDL_DEBUGF("First Thread: %d (%s)\n", 0, THREAD_SDL_GET_NAME(&threads[0])); }
/* Initialize SDL threading */ bool thread_sdl_init(void *param) { struct thread_entry *thread; int n; memset(cores, 0, sizeof(cores)); memset(threads, 0, sizeof(threads)); m = SDL_CreateMutex(); if (SDL_LockMutex(m) == -1) { fprintf(stderr, "Couldn't lock mutex\n"); return false; } /* Initialize all IDs */ for (n = 0; n < MAXTHREADS; n++) threads[n].id = THREAD_ID_INIT(n); /* Slot 0 is reserved for the main thread - initialize it here and then create the SDL thread - it is possible to have a quick, early shutdown try to access the structure. */ thread = &threads[0]; thread->stack = (uintptr_t *)" "; thread->stack_size = 8; thread->name = "main"; thread->state = STATE_RUNNING; thread->context.s = SDL_CreateSemaphore(0); cores[CURRENT_CORE].running = thread; if (thread->context.s == NULL) { fprintf(stderr, "Failed to create main semaphore\n"); return false; } thread->context.t = SDL_CreateThread(thread_sdl_app_main, param); if (thread->context.t == NULL) { SDL_DestroySemaphore(thread->context.s); fprintf(stderr, "Failed to create main thread\n"); return false; } THREAD_SDL_DEBUGF("Main thread: %p\n", thread); SDL_UnlockMutex(m); return true; }
int runthread(void *data) { struct thread_entry *current; jmp_buf *current_jmpbuf; /* Cannot access thread variables before locking the mutex as the data structures may not be filled-in yet. */ SDL_LockMutex(m); cores[CURRENT_CORE].running = (struct thread_entry *)data; current = cores[CURRENT_CORE].running; current_jmpbuf = &thread_jmpbufs[current - threads]; /* Setup jump for exit */ if (setjmp(*current_jmpbuf) == 0) { /* Run the thread routine */ if (current->state == STATE_FROZEN) { SDL_UnlockMutex(m); SDL_SemWait(current->context.s); SDL_LockMutex(m); cores[CURRENT_CORE].running = current; } if (threads_status == THREADS_RUN) { current->context.start(); THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n", current - threads, THREAD_SDL_GET_NAME(current)); /* Thread routine returned - suicide */ } thread_exit(); } else { /* Unlock and exit */ SDL_UnlockMutex(m); } return 0; }
void remove_thread(unsigned int thread_id) #endif { struct thread_entry *current = cores[CURRENT_CORE].running; struct thread_entry *thread = thread_id_entry(thread_id); SDL_Thread *t; SDL_sem *s; if (thread_id != THREAD_ID_CURRENT && thread->id != thread_id) return; int oldlevel = disable_irq_save(); t = thread->context.t; s = thread->context.s; /* Wait the last thread here and keep this one or SDL will leak it since * it doesn't free its own library allocations unless a wait is performed. * Such behavior guards against the memory being invalid by the time * SDL_WaitThread is reached and also against two different threads having * the same pointer. It also makes SDL_WaitThread a non-concurrent function. * * However, see more below about SDL_KillThread. */ SDL_WaitThread(thread->context.told, NULL); thread->context.t = NULL; thread->context.s = NULL; thread->context.told = t; if (thread != current) { switch (thread->state) { case STATE_BLOCKED: case STATE_BLOCKED_W_TMO: /* Remove thread from object it's waiting on */ remove_from_list_l(thread->bqp, thread); #ifdef HAVE_WAKEUP_EXT_CB if (thread->wakeup_ext_cb != NULL) thread->wakeup_ext_cb(thread); #endif break; } SDL_SemPost(s); } THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n", thread - threads, THREAD_SDL_GET_NAME(thread)); new_thread_id(thread->id, thread); thread->state = STATE_KILLED; thread_queue_wake(&thread->queue); SDL_DestroySemaphore(s); if (thread == current) { /* Do a graceful exit - perform the longjmp back into the thread function to return */ restore_irq(oldlevel); longjmp(thread_jmpbufs[current - threads], 1); } /* SDL_KillThread frees the old pointer too because it uses SDL_WaitThread * to wait for the host to remove it. */ thread->context.told = NULL; SDL_KillThread(t); restore_irq(oldlevel); }
/* Initialize SDL threading */ void init_threads(void) { struct thread_entry *thread; int n; memset(cores, 0, sizeof(cores)); memset(threads, 0, sizeof(threads)); m = SDL_CreateMutex(); if (SDL_LockMutex(m) == -1) { fprintf(stderr, "Couldn't lock mutex\n"); return; } /* Initialize all IDs */ for (n = 0; n < MAXTHREADS; n++) threads[n].id = THREAD_ID_INIT(n); /* Slot 0 is reserved for the main thread - initialize it here and then create the SDL thread - it is possible to have a quick, early shutdown try to access the structure. */ thread = &threads[0]; thread->stack = (uintptr_t *)" "; thread->stack_size = 8; thread->name = "main"; thread->state = STATE_RUNNING; thread->context.s = SDL_CreateSemaphore(0); thread->context.t = NULL; /* NULL for the implicit main thread */ cores[CURRENT_CORE].running = thread; if (thread->context.s == NULL) { fprintf(stderr, "Failed to create main semaphore\n"); return; } /* Tell all threads jump back to their start routines, unlock and exit gracefully - we'll check each one in turn for it's status. Threads _could_ terminate via remove_thread or multiple threads could exit on each unlock but that is safe. */ /* Setup jump for exit */ if (setjmp(thread_jmpbufs[0]) == 0) { THREAD_SDL_DEBUGF("Main thread: %p\n", thread); return; } SDL_UnlockMutex(m); /* Set to 'COMMAND_DONE' when other rockbox threads have exited. */ while (threads_status < THREADS_EXIT_COMMAND_DONE) SDL_Delay(10); SDL_DestroyMutex(m); /* We're the main thead - perform exit - doesn't return. */ sim_do_exit(); }