/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (the 'License'). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ /* * Thread.m * A simple Thread abstraction. * * Copyright (c) 1999, Apple Computer Inc. * All rights reserved. * * Created May 1999 by Marc Majka */ #import "Thread.h" #import "sys.h" #import #import #import #import extern uint32_t notify_set_state(int token, int state); extern uint32_t notify_register_plain(const char *name, int *out_token); static Thread **thread_list = NULL; static unsigned int initialized = 0; static unsigned int thread_count = 0; typedef struct { Thread *launchThread; SEL launchSEL; id launchContext; int launchArgCount; void *launchArg1; void *launchArg2; } launch_args; #ifdef _THREAD_TYPE_PTHREAD_ #define sys_mutex_type pthread_mutex_t #define thread_arg_type void * #define sys_mutex_lock pthread_mutex_lock #define sys_mutex_unlock pthread_mutex_unlock #else #define sys_mutex_type mutex_t #define thread_arg_type any_t #define sys_mutex_lock mutex_lock #define sys_mutex_unlock mutex_unlock #endif #ifdef _THREAD_TYPE_PTHREAD_ static sys_mutex_type *big_thread_lock; #else static sys_mutex_type big_thread_lock; #endif static thread_type sys_thread_self(void) { #ifdef _THREAD_TYPE_PTHREAD_ return pthread_self(); #else return cthread_self(); #endif } void lock_threads(void) { sys_mutex_lock(big_thread_lock); } void unlock_threads(void) { sys_mutex_unlock(big_thread_lock); } static void launchpad(launch_args *x) { launch_args args; args.launchThread = x->launchThread; args.launchSEL = x->launchSEL; args.launchContext = x->launchContext; args.launchArgCount = x->launchArgCount; args.launchArg1 = x->launchArg1; args.launchArg2 = x->launchArg2; free(x); [args.launchThread setThread:sys_thread_self()]; if (args.launchArgCount == 0) [args.launchContext perform:args.launchSEL]; else if (args.launchArgCount == 1) [args.launchContext perform:args.launchSEL with:args.launchArg1]; else if (args.launchArgCount == 2) [args.launchContext perform:args.launchSEL with:args.launchArg1 with:args.launchArg2]; } static void launch_thread(launch_args *args) { #ifdef _THREAD_TYPE_PTHREAD_ pthread_t t; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&t, &attr, (void *(*)(void *))launchpad, (void *)args); pthread_attr_destroy(&attr); sched_yield(); #else cthread_detach(cthread_fork((cthread_fn_t)launchpad, (any_t)args)); cthread_yield(); #endif } @implementation Thread /* * WARNING - threadAtIndex is used to enumerate threads. * call lock_threads() first, then iterate through threads, then * call unlock_threads(). */ + (Thread *)threadAtIndex:(unsigned int)i { if (i < thread_count) return thread_list[i]; return nil; } - (thread_type)thread { return thread; } - (void)setThread:(thread_type)t { thread = t; } + initialize { Thread *mainThread; if (initialized++ == 0) { #ifdef _THREAD_TYPE_PTHREAD_ big_thread_lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(big_thread_lock, NULL); #else big_thread_lock = mutex_alloc(); mutex_init(big_thread_lock); #endif mainThread = [[Thread alloc] init]; [mainThread setBanner:"Main Thread"]; mainThread->name = malloc(strlen("Main Thread") + 1); strcpy(mainThread->name, "Main Thread"); mainThread->shouldTerminate = NO; mainThread->isRunning = YES; mainThread->thread = sys_thread_self(); } return self; } + (void)shutdown { unsigned int i; lock_threads(); for (i = 0; i < thread_count; i++) [thread_list[i] release]; free(thread_list); unlock_threads(); #ifdef _THREAD_TYPE_PTHREAD_ pthread_mutex_destroy(big_thread_lock); free(big_thread_lock); #else mutex_free(big_thread_lock); #endif } + (Thread *)currentThread { thread_type me; unsigned int i; Thread *t; t = nil; lock_threads(); me = sys_thread_self(); for (i = 0; i < thread_count; i++) { if ([thread_list[i] thread] == me) { t = thread_list[i]; break; } } unlock_threads(); return t; } + (unsigned int)threadCount { unsigned int n; lock_threads(); n = thread_count; unlock_threads(); return n; } + (Thread *)threadWithName:(char *)n { unsigned int i; Thread *t; t = nil; lock_threads(); for (i = 0; i < thread_count; i++) { if (streq(thread_list[i]->name, n)) { t = thread_list[i]; break; } } unlock_threads(); return t; } + (void)releaseThread:(Thread *)t { unsigned int i, j; Thread *x; x = nil; lock_threads(); for (i = 0; i < thread_count; i++) { if (thread_list[i] == t) { x = thread_list[i]; for (j = i + 1; j < thread_count; j++) { thread_list[j - 1] = thread_list[j]; } thread_count--; if (thread_count == 0) { free(thread_list); thread_list = NULL; } else { thread_list = (Thread **)realloc(thread_list, thread_count * sizeof(Thread *)); } break; } } unlock_threads(); if (t != nil) [t release]; } + (void)threadExit { #ifdef _THREAD_TYPE_PTHREAD_ pthread_exit(NULL); #else cthread_exit(0); #endif } - (Thread *)init { unsigned int i; [super init]; lock_threads(); i = thread_count; thread_count++; if (thread_count == 1) { thread_list = (Thread **)malloc(sizeof(Thread *)); } else { thread_list = (Thread **)realloc(thread_list, thread_count * sizeof(Thread *)); } thread_list[i] = self; thread = (thread_type)-1; name = NULL; shouldTerminate = NO; isRunning = NO; data = NULL; dataLen = 0; server = NULL; context = NULL; state = ThreadStateInitial; notify_token = -1; unlock_threads(); return self; } - (void)dealloc { if (name != NULL) free(name); if (notify_token != -1) notify_cancel(notify_token); [super dealloc]; } - (unsigned int)state { return state; } - (void)setState:(unsigned int)s { char *str; int status; state = s; if (notify_token == -1) { str = NULL; asprintf(&str, "self.thread.%lu", (unsigned long)thread); if (str == NULL) return; status = notify_register_plain(str, ¬ify_token); free(str); if (status != NOTIFY_STATUS_OK) return; } notify_set_state(notify_token, s); } - (const char *)name { return name; } - (void)setName:(char *)n { if (name != NULL) free(name); name = NULL; if (n == NULL) return; [self setBanner:n]; name = malloc(strlen(n) + 1); strcpy(name, n); } - (void *)data { return data; } - (void)setData:(void *)d { data = d; } - (void *)server { return server; } - (void)setServer:(void *)s { server = s; } - (unsigned int)dataLen { return dataLen; } - (void)setDataLen:(unsigned int)l { dataLen = l; } - (void)run:(SEL)aSelector context:(id)anObject { launch_args *args; if (anObject == nil) return; if (![anObject respondsTo:aSelector]) return; if (isRunning) { [anObject perform:aSelector]; return; } context = [anObject retain]; isRunning = YES; args = (launch_args *)malloc(sizeof(launch_args)); args->launchThread = self; args->launchSEL = aSelector; args->launchContext = anObject; args->launchArgCount = 0; args->launchArg1 = NULL; args->launchArg2 = NULL; launch_thread(args); } - (void)run:(SEL)aSelector context:(id)anObject with:(id)arg1 { launch_args *args; if (anObject == nil) return; if (![anObject respondsTo:aSelector]) return; if (isRunning) { [anObject perform:aSelector with:arg1]; return; } context = [anObject retain]; isRunning = YES; args = (launch_args *)malloc(sizeof(launch_args)); args->launchThread = self; args->launchSEL = aSelector; args->launchContext = anObject; args->launchArgCount = 1; args->launchArg1 = arg1; args->launchArg2 = NULL; launch_thread(args); } - (void)run:(SEL)aSelector context:(id)anObject with:(id)arg1 with:(id)arg2 { launch_args *args; if (anObject == nil) return; if (![anObject respondsTo:aSelector]) return; if (isRunning) { [anObject perform:aSelector with:arg1 with:arg2]; return; } context = [anObject retain]; isRunning = YES; args = (launch_args *)malloc(sizeof(launch_args)); args->launchThread = self; args->launchSEL = aSelector; args->launchContext = anObject; args->launchArgCount = 2; args->launchArg1 = arg1; args->launchArg2 = arg2; launch_thread(args); } - (void)shouldTerminate:(BOOL)yn { shouldTerminate = yn; if (shouldTerminate) [self setState:ThreadStateExitRequested]; } - (BOOL)shouldTerminate { return shouldTerminate; } - (void)terminateSelf { [context release]; [Thread releaseThread:[Thread currentThread]]; [Thread threadExit]; } - (void)yield { #ifdef _THREAD_TYPE_PTHREAD_ sched_yield(); #else cthread_yield(); #endif } - (void)usleep:(unsigned int)msec { unsigned long oldState; sys_msg_timeout_type snooze; struct no_msg { sys_msg_header_type head; } no_msg; if (msec == 0) return; snooze = msec; sys_receive_port_alloc(&(no_msg.head), sizeof(no_msg)); oldState = state; state = ThreadStateSleeping; sys_receive_message(&(no_msg.head), sizeof(no_msg), no_msg.head.sys_msg_local_port, snooze); sys_port_free(no_msg.head.sys_msg_local_port); state = oldState; } - (void)sleep:(unsigned int)sec { if (sec == 0) return; [self usleep:sec * 1000]; } @end