/* Threading code. Copyright (C) 2011 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include #include #include "lisp.h" #include "buffer.h" #include "blockinput.h" #include #include #include "systime.h" #include "sysselect.h" void mark_catchlist (struct catchtag *); void mark_stack (char *, char *); void flush_stack_call_func (void (*) (char *, void *), void *); Lisp_Object Qthreadp; /* The main thread that exists when Emacs starts. */ static struct thread_state primary_thread; /* A linked list of all threads in existence. */ static struct thread_state *all_threads = &primary_thread; /* The Lisp thread object for the current thread. Note that this is thread-local. */ __thread struct thread_state *current_thread = &primary_thread; /* Only one thread can run Lisp code at a time. This thread holds the global lock. */ static pthread_mutex_t global_lock; static void mark_one_thread (struct thread_state *thread) { struct specbinding *bind; struct handler *handler; Lisp_Object tem; for (bind = thread->m_specpdl; bind != thread->m_specpdl_ptr; bind++) { mark_object (bind->symbol); mark_object (bind->old_value); } #if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \ || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS) mark_stack (thread->stack_bottom, thread->stack_top); #else { struct gcpro *tail; for (tail = thread->m_gcprolist; tail; tail = tail->next) for (i = 0; i < tail->nvars; i++) mark_object (tail->var[i]); } #endif #if BYTE_MARK_STACK if (thread->m_byte_stack_list) mark_byte_stack (thread->m_byte_stack_list); #endif mark_catchlist (thread->m_catchlist); for (handler = thread->m_handlerlist; handler; handler = handler->next) { mark_object (handler->handler); mark_object (handler->var); } #if BYTE_MARK_STACK mark_backtrace (thread->m_backtrace_list); #endif if (thread->m_current_buffer) { XSETBUFFER (tem, thread->m_current_buffer); mark_object (tem); } mark_object (thread->m_last_thing_searched); if (thread->m_saved_last_thing_searched) mark_object (thread->m_saved_last_thing_searched); } static void mark_threads_callback (char *end, void *ignore) { struct thread_state *iter; current_thread->stack_top = end; for (iter = all_threads; iter; iter = iter->next_thread) { Lisp_Object thread_obj; XSETTHREAD (thread_obj, iter); mark_object (thread_obj); mark_one_thread (iter); } } void mark_threads (void) { flush_stack_call_func (mark_threads_callback, NULL); } void unmark_threads (void) { struct thread_state *iter; for (iter = all_threads; iter; iter = iter->next_thread) if (iter->m_byte_stack_list) unmark_byte_stack (iter->m_byte_stack_list); } static void thread_yield_callback (char *end, void *ignore) { pthread_mutex_unlock (&global_lock); sched_yield (); pthread_mutex_lock (&global_lock); } void thread_yield (void) { flush_stack_call_func (thread_yield_callback, NULL); } DEFUN ("thread-yield", Fthread_yield, Sthread_yield, 0, 0, 0, doc: /* Yield to the next thread. */) (void) { thread_yield (); return other_threads_p () ? Qt : Qnil; } static Lisp_Object invoke_thread_function (void) { Lisp_Object iter; int count = SPECPDL_INDEX (); Ffuncall (1, ¤t_thread->func); return unbind_to (count, Qnil); } static Lisp_Object do_nothing (Lisp_Object whatever) { return whatever; } static void * run_thread (void *state) { struct thread_state *self = state; struct thread_state **iter; struct gcpro gcpro1; Lisp_Object buffer; char stack_pos; self->stack_top = self->stack_bottom = &stack_pos; self->m_specpdl_size = 50; self->m_specpdl = xmalloc (self->m_specpdl_size * sizeof (struct specbinding)); self->m_specpdl_ptr = self->m_specpdl; self->pthread_id = pthread_self (); /* Thread-local assignment. */ current_thread = self; /* We need special handling to set the initial buffer. Our parent thread is very likely to be using this same buffer so we will typically wait for the parent thread to release it first. */ XSETBUFFER (buffer, self->m_current_buffer); GCPRO1 (buffer); self->m_current_buffer = 0; pthread_mutex_lock (&global_lock); set_buffer_internal (XBUFFER (buffer)); /* It might be nice to do something with errors here. */ internal_condition_case (invoke_thread_function, Qt, do_nothing); { /*FIXME*/ extern void blocal_unbind_thread (Lisp_Object); blocal_unbind_thread (Fcurrent_thread ()); } /* Unlink this thread from the list of all threads. */ for (iter = &all_threads; *iter != self; iter = &(*iter)->next_thread) ; *iter = (*iter)->next_thread; xfree (self->m_specpdl); pthread_mutex_unlock (&global_lock); return NULL; } DEFUN ("make-thread", Fmake_thread, Smake_thread, 1, 2, 0, doc: /* Start a new thread and run FUNCTION in it. When the function exits, the thread dies. NAME is the name of the thread; it defaults to nil. */) (Lisp_Object function, Lisp_Object name) { char stack_pos; pthread_t thr; struct thread_state *new_thread; struct specbinding *p; /* Can't start a thread in temacs. */ if (!initialized) abort (); new_thread = ALLOCATE_PSEUDOVECTOR (struct thread_state, m_gcprolist, PVEC_THREAD); memset ((char *) new_thread + offsetof (struct thread_state, m_gcprolist), 0, sizeof (struct thread_state) - offsetof (struct thread_state, m_gcprolist)); new_thread->func = function; new_thread->name = name; new_thread->m_last_thing_searched = Qnil; /* copy from parent? */ new_thread->m_saved_last_thing_searched = Qnil; new_thread->m_current_buffer = current_thread->m_current_buffer; new_thread->stack_bottom = &stack_pos; initialize_globals (&new_thread->g); /* We'll need locking here. */ new_thread->next_thread = all_threads; all_threads = new_thread; if (pthread_create (&thr, NULL, run_thread, new_thread)) { /* Restore the previous situation. */ all_threads = all_threads->next_thread; error ("Could not start a new thread"); } return Qnil; } DEFUN ("current-thread", Fcurrent_thread, Scurrent_thread, 0, 0, 0, doc: /* Return the current thread. */) (void) { Lisp_Object result; XSETTHREAD (result, current_thread); return result; } DEFUN ("thread-name", Fthread_name, Sthread_name, 1, 1, 0, doc: /* Return the name of the THREAD. The name is the same object that was passed to `make-thread'. */) (Lisp_Object thread) { struct thread_state *tstate; CHECK_THREAD (thread); tstate = XTHREAD (thread); return tstate->name; } DEFUN ("all-threads", Fall_threads, Sall_threads, 0, 0, 0, doc: /* Return a list of all threads. */) (void) { Lisp_Object result = Qnil; struct thread_state *iter; for (iter = all_threads; iter; iter = iter->next_thread) { Lisp_Object thread; XSETTHREAD (thread, iter); result = Fcons (thread, result); } return result; } /* Get the main thread as a lisp object. */ Lisp_Object get_main_thread (void) { Lisp_Object result; XSETTHREAD (result, &primary_thread); return result; } /* Is the current an user thread. */ int user_thread_p (void) { struct thread_state *it = all_threads; pthread_t self = pthread_self (); do { if (it->pthread_id == self) return 1; } while (it = it->next_thread); return 0; } int thread_select (int n, SELECT_TYPE *rfd, SELECT_TYPE *wfd, SELECT_TYPE *xfd, EMACS_TIME *tmo) { char end; int ret; /* FIXME: must call flush_stack_call_func */ pthread_mutex_unlock (&global_lock); ret = select (n, rfd, wfd, xfd, tmo); pthread_mutex_lock (&global_lock); return ret; } int other_threads_p (void) { return all_threads->header.next.vector ? 1 : 0; } Lisp_Object thread_notify_kill_buffer (struct buffer *b) { Lisp_Object tem; struct thread_state *it = all_threads; for (; it; it = it->next_thread) { if (b == it->m_current_buffer) { Lisp_Object buf; XSETBUFFER (buf, it->m_current_buffer); tem = Fother_buffer (buf, Qnil, Qnil); it->m_current_buffer = XBUFFER (tem); if (b == it->m_current_buffer) return Qnil; } } return Qt; } void init_threads_once (void) { primary_thread.header.size = PSEUDOVECSIZE (struct thread_state, m_gcprolist); primary_thread.header.next.vector = NULL; primary_thread.func = Qnil; primary_thread.name = Qnil; XSETPVECTYPE (&primary_thread, PVEC_THREAD); } void init_threads (void) { pthread_mutex_init (&global_lock, NULL); pthread_mutex_lock (&global_lock); primary_thread.pthread_id = pthread_self (); primary_thread.m_last_thing_searched = Qnil; all_threads = &primary_thread; } void syms_of_threads (void) { defsubr (&Smake_thread); defsubr (&Sthread_yield); defsubr (&Scurrent_thread); defsubr (&Sthread_name); defsubr (&Sall_threads); DEFSYM (Qthreadp, "threadp"); }