tagged release 0.6.4
[parrot.git] / src / events.c
bloba3760fe08e68ff9f9d395b4c27c845fdc1501988
1 /*
2 Copyright (C) 2001-2008, The Perl Foundation.
3 $Id$
5 =head1 NAME
7 src/events.c - Event handling stuff
9 =head1 DESCRIPTION
11 An event_thread handles async events for all interpreters. When events
12 are due, they are placed in per interpreter task_queues, where they are
13 handled then by the C<check_event*> opcodes.
15 IO events and signals are caught in the io_thread, which again
16 dispatches these to one or all interpreters.
18 =over 4
20 =cut
24 #include "parrot/parrot.h"
25 #include "parrot/events.h"
27 typedef struct pending_io_events {
28 parrot_event **events;
29 size_t n;
30 size_t alloced;
31 } pending_io_events;
33 /* HEADERIZER HFILE: include/parrot/events.h */
34 /* HEADERIZER BEGIN: static */
35 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
37 PARROT_WARN_UNUSED_RESULT
38 PARROT_CAN_RETURN_NULL
39 static opcode_t * do_event(PARROT_INTERP,
40 ARGIN(parrot_event* event),
41 ARGIN_NULLOK(opcode_t *next))
42 __attribute__nonnull__(1)
43 __attribute__nonnull__(2);
45 PARROT_MALLOC
46 PARROT_CANNOT_RETURN_NULL
47 static QUEUE_ENTRY* dup_entry(ARGIN(const QUEUE_ENTRY *entry))
48 __attribute__nonnull__(1);
50 PARROT_WARN_UNUSED_RESULT
51 PARROT_CANNOT_RETURN_NULL
52 static QUEUE_ENTRY* dup_entry_interval(
53 ARGIN(QUEUE_ENTRY *entry),
54 FLOATVAL now)
55 __attribute__nonnull__(1);
57 PARROT_WARN_UNUSED_RESULT
58 PARROT_CAN_RETURN_NULL
59 static void* event_thread(ARGMOD(void *data))
60 __attribute__nonnull__(1)
61 FUNC_MODIFIES(*data);
63 static void event_to_exception(PARROT_INTERP,
64 ARGIN(const parrot_event* event))
65 __attribute__nonnull__(1)
66 __attribute__nonnull__(2);
68 static void init_events_all(PARROT_INTERP)
69 __attribute__nonnull__(1);
71 static void init_events_first(PARROT_INTERP)
72 __attribute__nonnull__(1);
74 PARROT_CAN_RETURN_NULL
75 static void* io_thread(SHIM(void *data));
77 static void io_thread_ready_rd(ARGMOD(pending_io_events *ios), int ready_rd)
78 __attribute__nonnull__(1)
79 FUNC_MODIFIES(*ios);
81 static void Parrot_sigaction(int sig, ARGIN(void (*handler)(int)))
82 __attribute__nonnull__(2);
84 static void Parrot_unblock_signal(int sig);
85 static int process_events(ARGMOD(QUEUE *event_q))
86 __attribute__nonnull__(1)
87 FUNC_MODIFIES(*event_q);
89 static void schedule_signal_event(int signum);
90 static void sig_handler(int signum);
91 static void stop_io_thread(void);
92 static void store_io_event(
93 ARGMOD(pending_io_events *ios),
94 ARGIN(parrot_event *ev))
95 __attribute__nonnull__(1)
96 __attribute__nonnull__(2)
97 FUNC_MODIFIES(*ios);
99 PARROT_WARN_UNUSED_RESULT
100 PARROT_CAN_RETURN_NULL
101 static opcode_t * wait_for_wakeup(PARROT_INTERP,
102 ARGIN_NULLOK(opcode_t *next))
103 __attribute__nonnull__(1);
105 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
106 /* HEADERIZER END: static */
109 * event debugging stuff - turn it off before running tests
111 #define EVENT_DEBUG 0
113 * not yet - need to sort out platform code and fix exceptions first
114 * TODO get some config for POSIX compliant
115 * TODO create API for extenders like ponie - events disabled for now
117 #if defined(linux) || defined(darwin)
118 # define INSTALL_EVENT_HANDLER 0
119 #else
120 # define INSTALL_EVENT_HANDLER 0
121 #endif
123 #if EVENT_DEBUG
124 # define edebug(x) fprintf (x)
125 static const char *ev_names[] = {
126 "EVENT_TYPE_NONE",
127 "EVENT_TYPE_EVENT",
128 "EVENT_TYPE_IO",
129 "EVENT_TYPE_MSG",
130 "EVENT_TYPE_TIMER",
131 "EVENT_TYPE_CALL_BACK",
132 "EVENT_TYPE_SLEEP",
133 "EVENT_TYPE_TERMINATE",
134 "EVENT_TYPE_EVENT_TERMINATE",
135 "EVENT_TYPE_CLASS_CHANGED",
136 "EVENT_TYPE_SIGNAL",
137 "EVENT_TYPE_SUSPEND_FOR_GC"
139 static const char*
140 et(const parrot_event* const e)
142 return ev_names[e->type];
145 #else
146 # define edebug(x)
147 #endif
150 /* forward defs */
153 * we have exactly one global event_queue
154 * TODO task prio handling
156 static QUEUE *event_queue;
157 #define TASK_PRIO 10
160 * user accessible signals like SIGINT
162 #ifndef SIGINT
163 # define SIGINT -4711
164 #endif
165 #ifndef SIGHUP
166 # define SIGHUP -4712
167 #endif
170 * XXX need a configure test
171 * should be sig_atomic_t
173 static int sig_int, sig_hup;
176 * a pipe is used to send messages to the IO thread
178 static int pipe_fds[2];
179 #define PIPE_READ_FD pipe_fds[0]
180 #define PIPE_WRITE_FD pipe_fds[1]
183 * a structure to communicate with the io_thread
185 typedef struct io_thread_msg {
186 INTVAL command;
187 parrot_event *ev;
188 } io_thread_msg;
193 =back
195 =head2 Signal Handling
197 =over 4
199 =item C<static void sig_handler>
201 Handle signal C<signum>.
203 TODO - Only C<SIGHUP> is handled at the moment for testing
205 =cut
209 static void
210 sig_handler(int signum)
212 switch (signum) {
213 case SIGINT:
214 sig_int = 1;
215 break;
216 case SIGHUP:
217 sig_hup = 1;
218 break;
219 default:
220 break;
226 =item C<static void Parrot_sigaction>
228 Signal handlers are common to all threads, signal block masks are
229 specific, so we install one handler then block that signal and unblock
230 it in the thread, that will receive that signal.
232 =cut
236 static void
237 Parrot_sigaction(int sig, ARGIN(void (*handler)(int)))
239 #ifdef PARROT_HAS_SIGACTION
240 struct sigaction action;
241 sigset_t block_mask;
243 /* install handler */
244 action.sa_handler = handler;
245 sigemptyset(&action.sa_mask);
246 action.sa_flags = 0;
247 sigaction(sig, &action, NULL);
249 /* block that signal */
250 sigemptyset(&block_mask);
251 sigaddset(&block_mask, sig);
252 sigprocmask(SIG_BLOCK, &block_mask, NULL);
253 #else
254 UNUSED(sig);
255 UNUSED(handler);
256 #endif
262 =item C<static void Parrot_unblock_signal>
264 unblock a signal
266 =cut
270 static void
271 Parrot_unblock_signal(int sig)
273 #ifdef PARROT_HAS_SIGACTION
274 sigset_t block_mask;
276 sigemptyset(&block_mask);
277 sigaddset(&block_mask, sig);
278 sigprocmask(SIG_UNBLOCK, &block_mask, NULL);
279 #else
280 UNUSED(sig);
281 #endif
287 =item C<void Parrot_init_signals>
289 Set up actions to handle signals.
290 Only SIGHUP handled at the moment.
292 =cut
296 PARROT_API
297 void
298 Parrot_init_signals(void)
301 * SIGFPE is architecture specific - some signal an error,
302 * some don't, so we have to use direct checks if we are dividing
303 * by zero.
305 Parrot_sigaction(SIGHUP, sig_handler);
310 =back
312 =head2 Initialization
314 =over 4
316 =item C<static void init_events_first>
318 Init event system for first interpreter.
320 =cut
324 static void
325 init_events_first(PARROT_INTERP)
327 Parrot_thread ev_handle;
328 #ifndef WIN32
329 Parrot_thread io_handle;
330 #endif
333 * be sure all init is done only once
334 * we could use pthread_once for that too
336 if (event_queue)
337 PANIC(interp, "event queue already exists - missing parent_interp?");
339 * create event queue
341 event_queue = queue_init(TASK_PRIO);
343 * we use a message pipe to send IO related stuff to the
344 * IO thread
346 #ifndef WIN32
348 * pipes on WIN32 don't support select
349 * s. p6i: "event.c - of signals and pipes"
351 if (pipe(pipe_fds))
352 real_exception(interp, NULL, 1, "Couldn't create message pipe");
353 #endif
355 * now set some sig handlers before any thread is started, so
356 * that all threads inherit the signal block mask
358 #if INSTALL_EVENT_HANDLER
359 Parrot_init_signals();
360 #endif
362 * we start an event_handler thread
364 THREAD_CREATE_DETACHED(ev_handle, event_thread, event_queue);
366 * and a signal and IO handler thread
368 #ifndef WIN32
369 THREAD_CREATE_DETACHED(io_handle, io_thread, event_queue);
370 #endif
375 =item C<static void init_events_all>
377 Init events for all interpreters.
379 =cut
383 static void
384 init_events_all(PARROT_INTERP)
387 * create per interpreter task queue
389 interp->task_queue = queue_init(0);
394 =item C<void Parrot_init_events>
396 Initialize the event system.
398 =cut
402 PARROT_API
403 void
404 Parrot_init_events(PARROT_INTERP)
406 if (!interp->parent_interpreter) {
407 /* add the very first interpreter to the list of interps. */
408 pt_add_to_interpreters(interp, NULL);
409 init_events_first(interp);
411 init_events_all(interp);
416 =back
418 =head2 Event Handler Functions
420 =over 4
422 =item C<void Parrot_schedule_event>
424 Create queue entry and insert event into task queue.
426 =cut
430 PARROT_API
431 void
432 Parrot_schedule_event(PARROT_INTERP, ARGMOD(parrot_event* ev))
434 QUEUE_ENTRY * const entry = mem_allocate_typed(QUEUE_ENTRY);
435 entry->next = NULL;
436 ev->interp = interp;
437 entry->data = ev;
438 switch (ev->type) {
439 case EVENT_TYPE_TIMER:
440 case EVENT_TYPE_SLEEP:
441 entry->type = QUEUE_ENTRY_TYPE_TIMED_EVENT;
442 insert_entry(event_queue, entry);
443 break;
444 case EVENT_TYPE_CALL_BACK:
445 case EVENT_TYPE_SIGNAL:
446 case EVENT_TYPE_IO:
447 entry->type = QUEUE_ENTRY_TYPE_EVENT;
448 unshift_entry(event_queue, entry);
449 break;
450 default:
451 entry->type = QUEUE_ENTRY_TYPE_EVENT;
452 push_entry(event_queue, entry);
453 break;
459 =item C<static void schedule_signal_event>
461 create and schedule a signal event
463 =cut
467 static void
468 schedule_signal_event(int signum)
470 parrot_event* const ev = mem_allocate_typed(parrot_event);
471 QUEUE_ENTRY * const entry = mem_allocate_typed(QUEUE_ENTRY);
473 entry->next = NULL;
474 entry->type = QUEUE_ENTRY_TYPE_EVENT;
475 ev->type = EVENT_TYPE_SIGNAL;
476 ev->u.signal = signum;
477 entry->data = ev;
479 * deliver to all interpreters
481 Parrot_schedule_broadcast_qentry(entry);
486 =item C<void Parrot_new_timer_event>
488 Create a new timer event due at C<diff> from now, repeated at C<interval>
489 and running the passed C<sub>.
491 =cut
495 PARROT_API
496 void
497 Parrot_new_timer_event(PARROT_INTERP, ARGIN_NULLOK(PMC *timer), FLOATVAL diff,
498 FLOATVAL interval, int repeat, ARGIN_NULLOK(PMC *sub), parrot_event_type_enum typ)
500 parrot_event* const ev = mem_allocate_typed(parrot_event);
502 const FLOATVAL now = Parrot_floatval_time();
504 ev->type = typ;
505 ev->u.timer_event.timer = timer;
506 ev->u.timer_event.abs_time = now + diff;
507 ev->u.timer_event.interval = interval;
508 ev->u.timer_event.repeat = repeat;
509 ev->u.timer_event.sub = sub;
511 if (repeat && FLOAT_IS_ZERO(interval))
512 ev->u.timer_event.interval = diff;
514 Parrot_schedule_event(interp, ev);
519 =item C<void Parrot_new_cb_event>
521 Prepare and schedule a callback event.
523 =cut
527 PARROT_API
528 void
529 Parrot_new_cb_event(PARROT_INTERP, ARGIN(PMC *cbi), ARGIN(char *ext))
531 parrot_event* const ev = mem_allocate_typed(parrot_event);
532 QUEUE_ENTRY* const entry = mem_allocate_typed(QUEUE_ENTRY);
534 entry->next = NULL;
535 entry->data = ev;
536 ev->interp = interp;
537 ev->type = EVENT_TYPE_CALL_BACK;
538 ev->u.call_back.cbi = cbi;
539 ev->u.call_back.external_data = ext;
540 Parrot_schedule_interp_qentry(interp, entry);
545 =item C<void Parrot_del_timer_event>
547 Deactivate the timer identified by C<timer>.
549 =cut
553 PARROT_API
554 void
555 Parrot_del_timer_event(PARROT_INTERP, ARGIN(const PMC *timer))
557 QUEUE_ENTRY *entry;
559 LOCK(event_queue->queue_mutex);
561 for (entry = event_queue->head; entry; entry = entry->next) {
562 if (entry->type == QUEUE_ENTRY_TYPE_TIMED_EVENT) {
564 parrot_event * const event = (parrot_event *)entry->data;
566 if (event->interp == interp
567 && event->u.timer_event.timer == timer) {
568 event->u.timer_event.interval = 0.0;
569 event->type = EVENT_TYPE_NONE;
570 break;
574 UNLOCK(event_queue->queue_mutex);
579 =item C<void Parrot_new_terminate_event>
581 Create a terminate event, interpreter will leave the run-loop when this
582 event arrives.
584 =cut
588 PARROT_API
589 void
590 Parrot_new_terminate_event(PARROT_INTERP)
592 parrot_event* const ev = mem_allocate_typed(parrot_event);
593 ev->type = EVENT_TYPE_TERMINATE;
594 Parrot_schedule_event(interp, ev);
599 =item C<void Parrot_new_suspend_for_gc_event>
601 Create a suspend-for-GC event, interpreter will wait on a condition
602 variable for GC to finish when the event arrives.
604 =cut
608 PARROT_API
609 void
610 Parrot_new_suspend_for_gc_event(PARROT_INTERP)
612 QUEUE_ENTRY *qe;
613 parrot_event* const ev = mem_allocate_typed(parrot_event);
614 ev->type = EVENT_TYPE_SUSPEND_FOR_GC;
615 qe = mem_allocate_typed(QUEUE_ENTRY);
616 qe->next = NULL;
617 qe->data = ev;
618 qe->type = QUEUE_ENTRY_TYPE_EVENT;
619 /* we don't use schedule_event because we must modify its
620 * task queue immediately
622 Parrot_schedule_interp_qentry(interp, qe);
627 =item C<void Parrot_kill_event_loop>
629 Schedule event-loop terminate event. This shuts down the event thread.
631 =cut
635 PARROT_API
636 void
637 Parrot_kill_event_loop(PARROT_INTERP)
639 parrot_event* const ev = mem_allocate_typed(parrot_event);
640 ev->type = EVENT_TYPE_EVENT_TERMINATE;
641 Parrot_schedule_event(interp, ev);
646 =item C<void Parrot_schedule_interp_qentry>
648 Put a queue entry into the interpreters task queue and enable event
649 checking for the interpreter.
651 =cut
655 PARROT_API
656 void
657 Parrot_schedule_interp_qentry(PARROT_INTERP, ARGIN(struct QUEUE_ENTRY *entry))
659 parrot_event * const event = (parrot_event *)entry->data;
661 * sleep checks events when it awakes
663 edebug((stderr, "got entry - schedule_inter_qentry %s\n", et(event)));
664 if (event->type != EVENT_TYPE_SLEEP)
665 enable_event_checking(interp);
667 * do push_entry last - this signales the queue condition so the
668 * interpreter might starting process that event immediately
670 * we should better use a priority for placing the event
671 * in front or at the end of the queue
673 switch (event->type) {
674 case EVENT_TYPE_CALL_BACK:
675 case EVENT_TYPE_SIGNAL:
676 unshift_entry(interp->task_queue, entry);
677 break;
678 default:
679 push_entry(interp->task_queue, entry);
680 break;
686 =item C<void Parrot_schedule_broadcast_qentry>
688 Broadcast an event.
690 =cut
694 void
695 Parrot_schedule_broadcast_qentry(ARGIN(struct QUEUE_ENTRY *entry))
697 parrot_event * const event = (parrot_event *)entry->data;
699 switch (event->type) {
700 case EVENT_TYPE_SIGNAL:
701 edebug((stderr, "broadcast signal\n"));
703 * we don't have special signal handlers in usercode yet
704 * e.g.:
705 * install handler like exception handler *and*
706 * set a interpreter flag, that a handler exists
707 * we then could examine that flag (after LOCKing it)
708 * and dispatch the exception to all interpreters that
709 * handle it
710 * Finally, we send the first (main) interpreter that signal
712 * For now just send to all.
715 switch (event->u.signal) {
716 case SIGHUP:
717 case SIGINT:
719 if (n_interpreters) {
720 size_t i;
721 LOCK(interpreter_array_mutex);
722 for (i = 1; i < n_interpreters; ++i) {
723 Interp *interp;
724 edebug((stderr, "deliver SIGINT to %d\n", i));
725 interp = interpreter_array[i];
726 if (interp)
727 Parrot_schedule_interp_qentry(interp,
728 dup_entry(entry));
730 UNLOCK(interpreter_array_mutex);
732 Parrot_schedule_interp_qentry(interpreter_array[0], entry);
733 edebug((stderr, "deliver SIGINT to 0\n"));
735 break;
736 default:
737 mem_sys_free(entry);
738 mem_sys_free(event);
740 break;
741 default:
742 mem_sys_free(entry);
743 mem_sys_free(event);
744 internal_exception(1, "Unknown event to broadcast");
745 break;
751 =back
753 =head2 IO Thread Handling
755 =over 4
757 =cut
761 #ifndef WIN32
765 =item C<static void store_io_event>
767 Stores an event in the event stack. Allocates memory if necessary.
769 =cut
773 static void
774 store_io_event(ARGMOD(pending_io_events *ios), ARGIN(parrot_event *ev))
776 if (!ios->alloced) {
777 ios->alloced = 16;
778 ios->events = mem_allocate_n_zeroed_typed(ios->alloced, parrot_event *);
780 else if (ios->n >= ios->alloced) {
781 ios->alloced *= 2;
782 mem_realloc_n_typed(ios->events, ios->alloced, parrot_event *);
784 ios->events[ios->n++] = ev;
789 =item C<static void io_thread_ready_rd>
791 Takes a list of pending i/o events and a file descriptor.
792 If the fd is ready to read, the event is removed from the
793 "pending" list and moved to the "scheduled" task queue.
795 =cut
799 static void
800 io_thread_ready_rd(ARGMOD(pending_io_events *ios), int ready_rd)
802 size_t i;
804 for (i = 0; i < ios->n; ++i) {
805 parrot_event * const ev = ios->events[i];
806 PMC * const pio = ev->u.io_event.pio;
807 const int fd = PIO_getfd(NULL, pio);
809 if (fd == ready_rd) {
810 /* remove from event list */
811 --ios->n;
813 for (; i < ios->n; ++i)
814 ios->events[i] = ios->events[i+1];
816 Parrot_schedule_event(ev->interp, ev);
817 break;
824 =item C<static void* io_thread>
826 The IO thread uses select/poll to handle IO events and signals.
828 It waits on input from the message pipe to insert file descriptors in
829 the wait sets.
831 =cut
835 PARROT_CAN_RETURN_NULL
836 static void*
837 io_thread(SHIM(void *data))
839 fd_set act_rfds, act_wfds;
840 int n_highest, i;
841 int running = 1;
842 pending_io_events ios;
844 ios.n = 0;
845 ios.alloced = 0;
846 ios.events = 0;
847 /* remember pending io events */
849 FD_ZERO(&act_rfds);
850 FD_ZERO(&act_wfds);
852 * Watch the reader end of the pipe for messages
854 FD_SET(PIPE_READ_FD, &act_rfds);
855 n_highest = PIPE_READ_FD + 1;
857 * all signals that we shall handle here have to be unblocked
858 * in this and only in this thread
860 Parrot_unblock_signal(SIGHUP);
861 while (running) {
862 fd_set rfds = act_rfds;
863 fd_set wfds = act_wfds;
864 const int retval = select(n_highest, &rfds, &wfds, NULL, NULL);
866 switch (retval) {
867 case -1:
868 if (errno == EINTR) {
869 edebug((stderr, "select EINTR\n"));
870 if (sig_int) {
871 edebug((stderr, "int arrived\n"));
872 sig_int = 0;
874 * signal the event thread
876 schedule_signal_event(SIGINT);
878 if (sig_hup) {
879 edebug((stderr, "int arrived\n"));
880 sig_hup = 0;
882 * signal the event thread
884 schedule_signal_event(SIGHUP);
888 break;
889 case 0: /* timeout - can't happen */
890 break;
891 default:
892 edebug((stderr, "IO ready\n"));
893 for (i = 0; i < n_highest; ++i) {
894 if (FD_ISSET(i, &rfds)) {
895 if (i == PIPE_READ_FD) {
896 io_thread_msg buf;
898 * a command arrived
900 edebug((stderr, "msg arrived\n"));
901 if (read(PIPE_READ_FD, &buf, sizeof (buf)) != sizeof (buf))
902 internal_exception(1,
903 "read error from msg pipe");
904 switch (buf.command) {
905 case IO_THR_MSG_TERMINATE:
906 running = 0;
907 break;
908 case IO_THR_MSG_ADD_SELECT_RD:
910 PMC * const pio = buf.ev->u.io_event.pio;
911 const int fd = PIO_getfd(NULL, pio);
912 if (FD_ISSET(fd, &act_rfds)) {
913 mem_sys_free(buf.ev);
914 break;
916 FD_SET(fd, &act_rfds);
917 if (fd >= n_highest)
918 n_highest = fd + 1;
919 store_io_event(&ios, buf.ev);
921 break;
922 /* TODO */
923 default:
924 internal_exception(1,
925 "unhandled msg in pipe");
926 break;
930 else {
932 * one of the io_event fds is ready
933 * remove from active set, as we don't
934 * want to fire again during io_handler
935 * invocation
937 FD_CLR(i, &act_rfds);
938 io_thread_ready_rd(&ios, i);
942 /* TODO check fds */
943 break;
946 edebug((stderr, "IO thread terminated\n"));
947 close(PIPE_READ_FD);
948 close(PIPE_WRITE_FD);
949 return NULL;
951 #endif
955 =item C<static void stop_io_thread>
957 Tell the IO thread to stop.
959 =cut
963 static void
964 stop_io_thread(void)
966 #ifndef WIN32
967 io_thread_msg buf;
969 * tell IO thread to stop
971 memset(&buf, 0, sizeof (buf));
972 buf.command = IO_THR_MSG_TERMINATE;
973 if (write(PIPE_WRITE_FD, &buf, sizeof (buf)) != sizeof (buf))
974 internal_exception(1, "msg pipe write failed");
975 #endif
980 =item C<void Parrot_event_add_io_event>
982 Create new i/o event.
984 =cut
988 PARROT_API
989 void
990 Parrot_event_add_io_event(PARROT_INTERP,
991 ARGIN_NULLOK(PMC *pio), ARGIN_NULLOK(PMC *sub), ARGIN_NULLOK(PMC *data), INTVAL which)
993 io_thread_msg buf;
994 parrot_event * const event = mem_allocate_typed(parrot_event);
996 event->type = EVENT_TYPE_IO;
997 event->interp = interp;
999 * TODO dod_register these PMCs as long as the event system
1000 * owns these 3
1001 * unregister, when event is passed to interp again
1003 event->u.io_event.pio = pio;
1004 event->u.io_event.handler = sub;
1005 event->u.io_event.user_data = data;
1007 buf.command = which;
1008 buf.ev = event;
1009 /* XXX Why isn't this entire function inside an ifndef WIN32? */
1010 #ifndef WIN32
1011 if (write(PIPE_WRITE_FD, &buf, sizeof (buf)) != sizeof (buf))
1012 real_exception(interp, NULL, 1, "msg pipe write failed");
1013 #endif
1019 =back
1021 =head2 Event Handler Thread Functions
1023 =over 4
1025 =item C<static QUEUE_ENTRY* dup_entry>
1027 Duplicate queue entry.
1029 =cut
1033 PARROT_MALLOC
1034 PARROT_CANNOT_RETURN_NULL
1035 static QUEUE_ENTRY*
1036 dup_entry(ARGIN(const QUEUE_ENTRY *entry))
1038 QUEUE_ENTRY * const new_entry = mem_allocate_typed(QUEUE_ENTRY);
1040 new_entry->next = NULL;
1041 new_entry->type = entry->type;
1042 new_entry->data = mem_allocate_typed(parrot_event);
1044 mem_sys_memcopy(new_entry->data, entry->data, sizeof (parrot_event));
1045 return new_entry;
1050 =item C<static QUEUE_ENTRY* dup_entry_interval>
1052 Duplicate timed entry and add interval to C<abs_time>.
1054 =cut
1058 PARROT_WARN_UNUSED_RESULT
1059 PARROT_CANNOT_RETURN_NULL
1060 static QUEUE_ENTRY*
1061 dup_entry_interval(ARGIN(QUEUE_ENTRY *entry), FLOATVAL now)
1063 QUEUE_ENTRY * const new_entry = dup_entry(entry);
1064 parrot_event * const event = (parrot_event *)new_entry->data;
1066 event->u.timer_event.abs_time = now + event->u.timer_event.interval;
1068 return new_entry;
1073 =item C<static int process_events>
1075 Do something, when an event arrived caller has locked the mutex returns
1076 0 if event thread terminates.
1078 =cut
1082 static int
1083 process_events(ARGMOD(QUEUE *event_q))
1085 FLOATVAL now;
1086 QUEUE_ENTRY *entry;
1088 while ((entry = peek_entry(event_q)) != NULL) {
1090 * one or more entries arrived - we hold the mutex again
1091 * so we have to use the nonsyc_pop_entry to pop off event entries
1093 parrot_event *event = NULL;
1095 switch (entry->type) {
1096 case QUEUE_ENTRY_TYPE_EVENT:
1097 entry = nosync_pop_entry(event_q);
1098 event = (parrot_event *)entry->data;
1099 break;
1101 case QUEUE_ENTRY_TYPE_TIMED_EVENT:
1102 event = (parrot_event *)entry->data;
1103 now = Parrot_floatval_time();
1106 * if the timer_event isn't due yet, ignore the event
1107 * (we were signalled on insert of the event)
1108 * wait until we get at it again when time has elapsed
1110 if (now < event->u.timer_event.abs_time)
1111 return 1;
1112 entry = nosync_pop_entry(event_q);
1114 /* if event is repeated dup and reinsert it */
1116 if (event->u.timer_event.interval) {
1117 if (event->u.timer_event.repeat) {
1118 if (event->u.timer_event.repeat != -1)
1119 event->u.timer_event.repeat--;
1120 nosync_insert_entry(event_q,
1121 dup_entry_interval(entry, now));
1124 break;
1125 default:
1126 internal_exception(1, "Unknown queue entry");
1128 PARROT_ASSERT(event);
1129 if (event->type == EVENT_TYPE_NONE) {
1130 mem_sys_free(entry);
1131 mem_sys_free(event);
1132 continue;
1134 else if (event->type == EVENT_TYPE_EVENT_TERMINATE) {
1135 mem_sys_free(entry);
1136 mem_sys_free(event);
1138 return 0;
1141 * now insert entry in interpreter task queue
1143 if (event->interp) {
1144 Parrot_schedule_interp_qentry(event->interp, entry);
1146 else {
1147 Parrot_schedule_broadcast_qentry(entry);
1149 } /* while events */
1150 return 1;
1155 =item C<static void* event_thread>
1157 The event thread is started by the first interpreter. It handles all
1158 events for all interpreters.
1160 =cut
1164 PARROT_WARN_UNUSED_RESULT
1165 PARROT_CAN_RETURN_NULL
1166 static void*
1167 event_thread(ARGMOD(void *data))
1169 QUEUE * const event_q = (QUEUE *) data;
1170 int running = 1;
1172 LOCK(event_q->queue_mutex);
1174 * we might already have an event in the queue
1176 if (peek_entry(event_q))
1177 running = process_events(event_q);
1178 while (running) {
1179 QUEUE_ENTRY * const entry = peek_entry(event_q);
1181 if (!entry) {
1182 /* wait infinite until entry arrives */
1183 queue_wait(event_q);
1185 else if (entry->type == QUEUE_ENTRY_TYPE_TIMED_EVENT) {
1186 /* do a_timedwait for entry */
1187 struct timespec abs_time;
1188 parrot_event * const event = (parrot_event*)entry->data;
1189 const FLOATVAL when = event->u.timer_event.abs_time;
1191 abs_time.tv_sec = (time_t) when;
1192 abs_time.tv_nsec = (long)((when - abs_time.tv_sec)*1000.0f)
1193 *1000L*1000L;
1194 queue_timedwait(event_q, &abs_time);
1196 else {
1197 /* we shouldn't get here probably
1199 internal_exception(1, "Spurious event");
1203 * one or more entries arrived - we hold the mutex again
1204 * so we have to use the nonsync_pop_entry to pop off event entries
1206 running = process_events(event_q);
1207 } /* event loop */
1209 * the main interpreter is dying
1210 * TODO empty the queue
1212 UNLOCK(event_q->queue_mutex);
1213 queue_destroy(event_q);
1214 stop_io_thread();
1215 edebug((stderr, "event thread stopped\n"));
1216 return NULL;
1221 =back
1223 =head2 Sleep Handling
1225 =over 4
1227 =item C<static opcode_t * wait_for_wakeup>
1229 Sleep on the event queue condition. If an event arrives, the event
1230 is processed. Terminate the loop if sleeping is finished.
1232 =cut
1236 PARROT_WARN_UNUSED_RESULT
1237 PARROT_CAN_RETURN_NULL
1238 static opcode_t *
1239 wait_for_wakeup(PARROT_INTERP, ARGIN_NULLOK(opcode_t *next))
1241 QUEUE * const tq = interp->task_queue;
1243 interp->sleeping = 1;
1246 * event handler like callbacks or timers are run as normal code
1247 * so inside such an event handler function, another event might get
1248 * handled, which is good (higher priority events can interrupt
1249 * other event handler). OTOH we must ensure that all state changes
1250 * are done in do_event and we should probably suspend nested
1251 * event handlers sometimes
1253 * FIXME: the same is true for the *next param:
1254 * get rid of that, instead mangle the resume flags
1255 * and offset to stop the runloop
1259 while (interp->sleeping) {
1260 QUEUE_ENTRY * const entry = wait_for_entry(tq);
1261 parrot_event * const event = (parrot_event*)entry->data;
1263 mem_sys_free(entry);
1264 edebug((stderr, "got ev %s head : %p\n", et(event), tq->head));
1265 next = do_event(interp, event, next);
1268 edebug((stderr, "woke up\n"));
1269 return next;
1274 =item C<opcode_t * Parrot_sleep_on_event>
1276 Go to sleep. This is called from the C<sleep> opcode.
1278 =cut
1282 PARROT_API
1283 PARROT_WARN_UNUSED_RESULT
1284 PARROT_CAN_RETURN_NULL
1285 opcode_t *
1286 Parrot_sleep_on_event(PARROT_INTERP, FLOATVAL t, ARGIN_NULLOK(opcode_t *next))
1288 #if PARROT_HAS_THREADS
1290 if (interp->sleeping)
1291 fprintf(stderr, "nested sleep might not work\n");
1293 * place the opcode_t* next arg in the event data, so that
1294 * we can identify this event in wakeup
1296 Parrot_new_timer_event(interp, (PMC *) next, t,
1297 0, 0, NULL, EVENT_TYPE_SLEEP);
1298 next = wait_for_wakeup(interp, next);
1299 #else
1301 * TODO check for nanosleep or such
1303 Parrot_sleep((UINTVAL) ceil(t));
1304 #endif
1305 return next;
1310 =back
1312 =head2 Event Handling for Run-Loops
1314 =over 4
1316 =item C<opcode_t * Parrot_do_check_events>
1318 Explicitly C<sync> called by the check_event opcode from run loops.
1320 =cut
1324 PARROT_API
1325 PARROT_WARN_UNUSED_RESULT
1326 PARROT_CAN_RETURN_NULL
1327 opcode_t *
1328 Parrot_do_check_events(PARROT_INTERP, ARGIN_NULLOK(opcode_t *next))
1330 if (peek_entry(interp->task_queue))
1331 return Parrot_do_handle_events(interp, 0, next);
1333 return next;
1338 =item C<static void event_to_exception>
1340 Convert event to exception and throw it.
1342 =cut
1346 static void
1347 event_to_exception(PARROT_INTERP, ARGIN(const parrot_event* event))
1349 const int exit_code = -event->u.signal;
1351 switch (event->u.signal) {
1352 case SIGINT:
1353 case SIGHUP:
1355 * SIGINT is silent, if no exception handler is
1356 * installed: set severity to EXCEPT_exit
1358 do_exception(interp, EXCEPT_exit, exit_code);
1359 break;
1360 default:
1361 do_exception(interp, EXCEPT_error, exit_code);
1362 break;
1368 =item C<static opcode_t * do_event>
1370 Run user code or such. The C<event> argument is freed after execution.
1372 TODO: Instrument with splint args so splint knows event gets released.
1374 =cut
1378 PARROT_WARN_UNUSED_RESULT
1379 PARROT_CAN_RETURN_NULL
1380 static opcode_t *
1381 do_event(PARROT_INTERP, ARGIN(parrot_event* event), ARGIN_NULLOK(opcode_t *next))
1383 edebug((stderr, "do_event %s\n", et(event)));
1384 switch (event->type) {
1385 case EVENT_TYPE_TERMINATE:
1386 next = NULL; /* this will terminate the run loop */
1387 break;
1388 case EVENT_TYPE_SIGNAL:
1389 interp->sleeping = 0;
1390 /* generate exception */
1391 event_to_exception(interp, event);
1392 /* not reached - will longjmp */
1393 break;
1394 case EVENT_TYPE_TIMER:
1395 /* run ops, save registers */
1396 Parrot_runops_fromc_args_event(interp,
1397 event->u.timer_event.sub, "v");
1398 break;
1399 case EVENT_TYPE_CALL_BACK:
1400 edebug((stderr, "starting user cb\n"));
1401 Parrot_run_callback(interp, event->u.call_back.cbi,
1402 event->u.call_back.external_data);
1403 break;
1404 case EVENT_TYPE_IO:
1405 edebug((stderr, "starting io handler\n"));
1406 Parrot_runops_fromc_args_event(interp,
1407 event->u.io_event.handler,
1408 "vPP",
1409 event->u.io_event.pio,
1410 event->u.io_event.user_data);
1411 break;
1412 case EVENT_TYPE_SLEEP:
1413 interp->sleeping = 0;
1414 break;
1415 case EVENT_TYPE_SUSPEND_FOR_GC:
1416 edebug((stderr, "suspend for gc\n"));
1417 pt_suspend_self_for_gc(interp);
1418 break;
1419 default:
1420 fprintf(stderr, "Unhandled event type %d\n", (int)event->type);
1421 break;
1423 mem_sys_free(event);
1424 return next;
1429 =item C<opcode_t * Parrot_do_handle_events>
1431 Called by the C<check_event__> opcode from run loops or from above. When
1432 called from the C<check_events__> opcode, we have to restore the
1433 C<op_func_table>.
1435 =cut
1439 PARROT_API
1440 PARROT_WARN_UNUSED_RESULT
1441 PARROT_CAN_RETURN_NULL
1442 opcode_t *
1443 Parrot_do_handle_events(PARROT_INTERP, int restore, ARGIN_NULLOK(opcode_t *next))
1445 QUEUE * const tq = interp->task_queue;
1447 if (restore)
1448 disable_event_checking(interp);
1450 if (!peek_entry(tq))
1451 return next;
1453 while (peek_entry(tq)) {
1454 QUEUE_ENTRY * const entry = pop_entry(tq);
1455 parrot_event * const event = (parrot_event*)entry->data;
1457 mem_sys_free(entry);
1458 next = do_event(interp, event, next);
1461 return next;
1466 =back
1468 =head1 SEE ALSO
1470 F<include/parrot/events.h> and F<docs/dev/events.pod>.
1472 =cut
1478 * Local variables:
1479 * c-file-style: "parrot"
1480 * End:
1481 * vim: expandtab shiftwidth=4: