[svn:parrot] r47951 - in branches/gsoc_threads: config/gen/platform/generic include/parrot src src/gc src/pmc t/pmc

Chandon at svn.parrot.org Chandon at svn.parrot.org
Thu Jul 1 22:20:45 UTC 2010


Author: Chandon
Date: Thu Jul  1 22:20:44 2010
New Revision: 47951
URL: https://trac.parrot.org/parrot/changeset/47951

Log:
[gsoc_threads] Alarms work, at least the easy way.

Added:
   branches/gsoc_threads/t/pmc/alarm.t   (contents, props changed)
Modified:
   branches/gsoc_threads/config/gen/platform/generic/time.c
   branches/gsoc_threads/include/parrot/alarm_private.h
   branches/gsoc_threads/include/parrot/scheduler.h
   branches/gsoc_threads/include/parrot/scheduler_private.h
   branches/gsoc_threads/src/alarm.c
   branches/gsoc_threads/src/gc/gc_ms.c
   branches/gsoc_threads/src/pmc/alarm.pmc
   branches/gsoc_threads/src/pmc/pmclist.pmc
   branches/gsoc_threads/src/pmc/scheduler.pmc
   branches/gsoc_threads/src/scheduler.c

Modified: branches/gsoc_threads/config/gen/platform/generic/time.c
==============================================================================
--- branches/gsoc_threads/config/gen/platform/generic/time.c	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/config/gen/platform/generic/time.c	Thu Jul  1 22:20:44 2010	(r47951)
@@ -94,6 +94,28 @@
 
 /*
 
+=item C<FLOATVAL Parrot_floatval_sleep(void)>
+
+Parrot wrapper around standard library C<time()> function, returning a FLOATVAL.
+
+=cut
+
+*/
+
+void
+Parrot_floatval_sleep(FLOATVAL time)
+{
+    if (time > 1000) {
+        /* prevent integer overflow when converting to microseconds */
+        const int seconds = floor(time);
+        Parrot_sleep(seconds);
+        time -= seconds;
+    }
+    Parrot_usleep((UINTVAL) time*1000000);
+}
+
+/*
+
 =item C<struct tm * Parrot_gmtime_r(const time_t *t, struct tm *tm)>
 
 Parrot wrapper around standard library C<gmtime_r()> function.

Modified: branches/gsoc_threads/include/parrot/alarm_private.h
==============================================================================
--- branches/gsoc_threads/include/parrot/alarm_private.h	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/include/parrot/alarm_private.h	Thu Jul  1 22:20:44 2010	(r47951)
@@ -10,7 +10,6 @@
 
 typedef struct Parrot_alarm_queue {
     FLOATVAL when;
-    int alarm_set;
     struct Parrot_alarm_queue *next;
 } Parrot_alarm_queue;
 

Modified: branches/gsoc_threads/include/parrot/scheduler.h
==============================================================================
--- branches/gsoc_threads/include/parrot/scheduler.h	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/include/parrot/scheduler.h	Thu Jul  1 22:20:44 2010	(r47951)
@@ -15,6 +15,8 @@
 
 #include "parrot/parrot.h"
 
+#define PARROT_TASK_SWITCH_QUANTUM 0.02
+
 /* HEADERIZER BEGIN: src/scheduler.c */
 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
 
@@ -82,6 +84,12 @@
         __attribute__nonnull__(2);
 
 PARROT_EXPORT
+void Parrot_cx_handle_alarms(PARROT_INTERP, ARGMOD(PMC *scheduler))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        FUNC_MODIFIES(*scheduler);
+
+PARROT_EXPORT
 void Parrot_cx_handle_tasks(PARROT_INTERP, ARGMOD(PMC *scheduler))
         __attribute__nonnull__(1)
         __attribute__nonnull__(2)
@@ -114,6 +122,17 @@
         __attribute__nonnull__(3);
 
 PARROT_EXPORT
+void Parrot_cx_schedule_immediate(PARROT_INTERP, ARGIN(PMC *sub))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2);
+
+PARROT_EXPORT
+void Parrot_cx_schedule_next_task(PARROT_INTERP, ARGMOD(PMC *scheduler))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        FUNC_MODIFIES(*scheduler);
+
+PARROT_EXPORT
 void Parrot_cx_schedule_repeat(PARROT_INTERP, ARGIN(PMC *task))
         __attribute__nonnull__(1)
         __attribute__nonnull__(2);
@@ -211,6 +230,9 @@
 #define ASSERT_ARGS_Parrot_cx_find_handler_local __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(task))
+#define ASSERT_ARGS_Parrot_cx_handle_alarms __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(scheduler))
 #define ASSERT_ARGS_Parrot_cx_handle_tasks __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(scheduler))
@@ -228,6 +250,12 @@
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(user_data) \
     , PARROT_ASSERT_ARG(ext_data))
+#define ASSERT_ARGS_Parrot_cx_schedule_immediate __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(sub))
+#define ASSERT_ARGS_Parrot_cx_schedule_next_task __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(scheduler))
 #define ASSERT_ARGS_Parrot_cx_schedule_repeat __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(task))

Modified: branches/gsoc_threads/include/parrot/scheduler_private.h
==============================================================================
--- branches/gsoc_threads/include/parrot/scheduler_private.h	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/include/parrot/scheduler_private.h	Thu Jul  1 22:20:44 2010	(r47951)
@@ -22,6 +22,8 @@
     SCHEDULER_terminate_requested_FLAG = PObj_private2_FLAG
 } scheduler_flags_enum;
 
+void Parrot_pmc_list_insert_by_number(PARROT_INTERP, PMC *list, PMC *value);
+
 #define SCHEDULER_get_FLAGS(o) (PObj_get_FLAGS(o))
 #define SCHEDULER_flag_TEST(flag, o) (SCHEDULER_get_FLAGS(o) & SCHEDULER_ ## flag ## _FLAG)
 #define SCHEDULER_flag_SET(flag, o) (SCHEDULER_get_FLAGS(o) |= SCHEDULER_ ## flag ## _FLAG)

Modified: branches/gsoc_threads/src/alarm.c
==============================================================================
--- branches/gsoc_threads/src/alarm.c	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/src/alarm.c	Thu Jul  1 22:20:44 2010	(r47951)
@@ -85,8 +85,6 @@
     itmr.it_interval.tv_sec  = 0;
     itmr.it_interval.tv_usec = 0;
 
-    fprintf(stderr, "setitimer(%.02lf): sec = %d; usec = %d\n", wait, sec, usec);
-    
     if(setitimer(ITIMER_REAL, &itmr, 0) == -1) {
         perror("setitimer failed in set_posix_alarm");
         exit(EXIT_FAILURE);
@@ -108,8 +106,6 @@
     FLOATVAL now, wait;
     Parrot_alarm_queue* qp;
 
-    fprintf(stderr, "Got Parrot_timers_alarm_callback()\n");
-
     /* Not atomic; only one thread ever writes this value */
     alarm_serial += 1;
 
@@ -121,10 +117,9 @@
         alarm_queue = qp;
     }
 
-    if(alarm_queue != NULL && alarm_queue->alarm_set == 0) {
+    if(alarm_queue != NULL) {
         wait = alarm_queue->when - now;
         set_posix_alarm(wait);
-        qp->alarm_set = 1;
     }
 }
 
@@ -176,7 +171,6 @@
 
     if(alarm_queue == NULL || when < alarm_queue->when) {
         new_alarm->next = alarm_queue;
-        new_alarm->alarm_set = 1;
         alarm_queue = new_alarm;
         set_posix_alarm(when - now);
         return;
@@ -188,7 +182,6 @@
     }
 
     new_alarm->next = *qpp;
-    new_alarm->alarm_set = 0;
     *qpp = new_alarm;
 }
 

Modified: branches/gsoc_threads/src/gc/gc_ms.c
==============================================================================
--- branches/gsoc_threads/src/gc/gc_ms.c	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/src/gc/gc_ms.c	Thu Jul  1 22:20:44 2010	(r47951)
@@ -636,11 +636,14 @@
 
     /* keep the scheduler and its kids alive for Task-like PMCs to destroy
      * themselves; run a sweep to collect them */
+    /* TODO: Figure out if this stuff was important */
+#ifdef PARROT_CX_BUILD_OLD_STUFF
     if (interp->scheduler) {
         Parrot_gc_mark_PMC_alive(interp, interp->scheduler);
         VTABLE_mark(interp, interp->scheduler);
         Parrot_gc_sweep_pool(interp, mem_pools, mem_pools->pmc_pool);
     }
+#endif
 
     /* now sweep everything that's left */
     Parrot_gc_sweep_pool(interp, mem_pools, mem_pools->pmc_pool);

Modified: branches/gsoc_threads/src/pmc/alarm.pmc
==============================================================================
--- branches/gsoc_threads/src/pmc/alarm.pmc	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/src/pmc/alarm.pmc	Thu Jul  1 22:20:44 2010	(r47951)
@@ -48,6 +48,10 @@
 */
 
     VTABLE void init() {
+        Parrot_Alarm_attributes *data = PARROT_ALARM(SELF);
+        data->alarm_time = 0.0;
+        data->alarm_sub  = PMCNULL;
+
         PObj_custom_mark_SET(SELF);
     }
 

Modified: branches/gsoc_threads/src/pmc/pmclist.pmc
==============================================================================
--- branches/gsoc_threads/src/pmc/pmclist.pmc	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/src/pmc/pmclist.pmc	Thu Jul  1 22:20:44 2010	(r47951)
@@ -480,10 +480,6 @@
 =cut
 */
 
-void debug_my_face() { 
-    fprintf(stderr, "");
-};
-
 PARROT_EXPORT
 void 
 Parrot_pmc_list_insert_by_number(PARROT_INTERP, PMC *list, PMC *value) {

Modified: branches/gsoc_threads/src/pmc/scheduler.pmc
==============================================================================
--- branches/gsoc_threads/src/pmc/scheduler.pmc	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/src/pmc/scheduler.pmc	Thu Jul  1 22:20:44 2010	(r47951)
@@ -38,7 +38,8 @@
     ATTR PMC          *messages;     /* A message queue used for communication
                                         between schedulers. */
 
-    ATTR PMC          *alarms;       /* Array of future alarms ordered by time */
+    ATTR PMC          *task_queue;   /* List of tasks/green threads waiting to run */
+    ATTR PMC          *alarms;       /* List of future alarms ordered by time */
 
     ATTR Parrot_mutex  msg_lock;     /* Lock to synchronize the message queue. */
     ATTR Parrot_Interp interp;       /* A link to the scheduler's interpreter. */
@@ -69,6 +70,7 @@
         core_struct->wait_index  = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
         core_struct->handlers    = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
         core_struct->messages    = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
+        core_struct->task_queue  = Parrot_pmc_new(INTERP, enum_class_PMCList);
         core_struct->alarms      = Parrot_pmc_new(INTERP, enum_class_PMCList);
         core_struct->interp      = INTERP;
         MUTEX_INIT(core_struct->msg_lock);
@@ -281,12 +283,8 @@
             Parrot_gc_mark_PMC_alive(INTERP, core_struct->wait_index);
             Parrot_gc_mark_PMC_alive(INTERP, core_struct->handlers);
             Parrot_gc_mark_PMC_alive(INTERP, core_struct->messages);
-
-            if(PObj_is_PMC_TEST(core_struct->alarms)) {
-                Parrot_gc_mark_PMC_alive(INTERP, core_struct->alarms);
-            } else {
-                fprintf(stderr, "Why is this thing not a PMC?\n");
-            }
+            Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_queue);
+            Parrot_gc_mark_PMC_alive(INTERP, core_struct->alarms);
         }
     }
 

Modified: branches/gsoc_threads/src/scheduler.c
==============================================================================
--- branches/gsoc_threads/src/scheduler.c	Thu Jul  1 21:51:08 2010	(r47950)
+++ branches/gsoc_threads/src/scheduler.c	Thu Jul  1 22:20:44 2010	(r47951)
@@ -20,11 +20,13 @@
 #include "parrot/scheduler_private.h"
 #include "parrot/runcore_api.h"
 #include "parrot/alarm.h"
+#include "parrot/scheduler.h"
 
 #include "pmc/pmc_scheduler.h"
 #include "pmc/pmc_task.h"
 #include "pmc/pmc_timer.h"
 #include "pmc/pmc_alarm.h"
+#include "pmc/pmc_pmclist.h"
 
 #include "scheduler.str"
 
@@ -136,9 +138,15 @@
 {
     ASSERT_ARGS(Parrot_cx_handle_tasks)
 
-    
-
     SCHEDULER_wake_requested_CLEAR(scheduler);
+
+    Parrot_cx_handle_alarms(interp, scheduler);
+    // Parrot_cx_check_quantum(interp)
+
+    if(SCHEDULER_wake_requested_TEST(scheduler))
+        Parrot_cx_schedule_next_task(interp, scheduler);
+
+#ifdef PARROT_CX_BUILD_OLD_STUFF
     Parrot_cx_refresh_task_list(interp, scheduler);
 
     while (VTABLE_get_integer(interp, scheduler) > 0) {
@@ -175,6 +183,27 @@
             Parrot_cx_refresh_task_list(interp, scheduler);
 
     } /* end of pending tasks */
+#endif
+}
+
+/*
+=item C<void Parrot_cx_schedule_next_task(PARROT_INTERP, PMC *scheduler)>
+
+Put the current task on the foot of the task queue and schedule whatever
+is on the head, then reset the rescheduling quantum.
+
+TODO: Make the above true rather than a dirty lie.
+
+=end
+*/
+
+PARROT_EXPORT
+void
+Parrot_cx_schedule_next_task(PARROT_INTERP, ARGMOD(PMC *scheduler))
+{
+    Parrot_Scheduler_attributes *sched = PARROT_SCHEDULER(interp->scheduler);
+    PMC *sub = VTABLE_shift_pmc(interp, sched->task_queue);
+    Parrot_pcc_invoke_sub_from_c_args(interp, sub, "->");
 }
 
 /*
@@ -238,7 +267,9 @@
 {
     ASSERT_ARGS(Parrot_cx_runloop_end)
     SCHEDULER_terminate_requested_SET(interp->scheduler);
-    Parrot_cx_handle_tasks(interp, interp->scheduler);
+
+    /* TODO: Figure out why this was here, fix it if useful. */
+    /* Parrot_cx_handle_tasks(interp, interp->scheduler); */
 }
 
 /*
@@ -976,6 +1007,26 @@
 
 /*
 
+=item C<void Parrot_cx_schedule_immediate(PARROT_INTERP, PMC *sub) >
+
+Add a task to the task queue for immediate execution.
+
+=cut
+
+*/
+
+PARROT_EXPORT
+void
+Parrot_cx_schedule_immediate(PARROT_INTERP, ARGIN(PMC *sub)) 
+{
+    ASSERT_ARGS(Parrot_cx_schedule_immediate)
+    Parrot_Scheduler_attributes *sched = PARROT_SCHEDULER(interp->scheduler);
+    VTABLE_unshift_pmc(interp, sched->task_queue, sub);
+    SCHEDULER_wake_requested_SET(interp->scheduler);    
+}
+
+/*
+
 =item C<void Parrot_cx_schedule_alarm(PARROT_INTERP, PMC *alarm)>
 
 Schedule an alarm.
@@ -989,22 +1040,49 @@
 Parrot_cx_schedule_alarm(PARROT_INTERP, ARGIN(PMC *alarm))
 {
     ASSERT_ARGS(Parrot_cx_schedule_alarm)
-    FLOATVAL alarm_time; 
-    int i;
-    PMC *tmp1;
-    PMC *tmp2;
 
     Parrot_Scheduler_attributes *sched = PARROT_SCHEDULER(interp->scheduler);
-
-    alarm_time = VTABLE_get_number(interp, alarm);
-
-    fprintf(stderr, "Schedule alarm for %.02f\n", alarm_time);
+    FLOATVAL alarm_time = VTABLE_get_number(interp, alarm);
     Parrot_alarm_set(alarm_time);
 
     /* Insert new alarm at correct (ordered by time) position in array. */
     Parrot_pmc_list_insert_by_number(interp, sched->alarms, alarm);
 }
 
+/*
+
+=item C<void Parrot_cx_handle_alarms(PARROT_INTERP, PMC *scheduler) >
+
+Add the subs attached to any expired alarms to the task queue.
+
+=cut
+
+*/
+
+PARROT_EXPORT
+void
+Parrot_cx_handle_alarms(PARROT_INTERP, ARGMOD(PMC *scheduler)) 
+{
+    ASSERT_ARGS(Parrot_cx_handle_alarms)
+    Parrot_Scheduler_attributes *sched = PARROT_SCHEDULER(interp->scheduler);
+    INTVAL   alarm_count = VTABLE_get_integer(interp, sched->alarms);
+    FLOATVAL now_time    = Parrot_floatval_time();
+
+    while(alarm_count) {
+        PMC *alarm = VTABLE_shift_pmc(interp, sched->alarms);
+        FLOATVAL alarm_time = VTABLE_get_number(interp, alarm);
+
+        if(alarm_time < now_time) {
+            Parrot_Alarm_attributes *data = PARROT_ALARM(alarm);
+            Parrot_cx_schedule_immediate(interp, data->alarm_sub);
+        } else {
+            VTABLE_unshift_pmc(interp, sched->alarms, alarm);
+            break;
+        }
+
+        alarm_count--;
+    }
+}
 
 /*
 
@@ -1056,6 +1134,27 @@
 Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time, ARGIN_NULLOK(opcode_t *next))
 {
     ASSERT_ARGS(Parrot_cx_schedule_sleep)
+    Parrot_Scheduler_attributes *sched = PARROT_SCHEDULER(interp->scheduler);
+    FLOATVAL now_time   = Parrot_floatval_time();
+    FLOATVAL done_time  = now_time + time;
+    FLOATVAL alarm_time = done_time;
+    INTVAL   alarm_count;
+    PMC      *alarm;
+
+    while(now_time < done_time) {
+        alarm_count = VTABLE_get_integer(interp, sched->alarms);
+        
+        if(alarm_count > 0) {
+            alarm = VTABLE_shift_pmc(interp, sched->alarms);
+            alarm_time = VTABLE_get_number(interp, alarm);
+            VTABLE_unshift_pmc(interp, sched->alarms, alarm);
+        }
+
+        Parrot_floatval_sleep(done_time - fmin(done_time, alarm_time));
+        Parrot_cx_check_tasks(interp, interp->scheduler);
+    }
+
+#ifdef PARROT_CX_BUILD_OLD_STUFF
 #ifdef PARROT_HAS_THREADS
     Parrot_cond condition;
     Parrot_mutex lock;
@@ -1086,6 +1185,10 @@
     }
     Parrot_usleep((UINTVAL) time*1000000);
 #endif
+#endif
+
+    
+
     return next;
 }
 

Added: branches/gsoc_threads/t/pmc/alarm.t
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ branches/gsoc_threads/t/pmc/alarm.t	Thu Jul  1 22:20:44 2010	(r47951)
@@ -0,0 +1,149 @@
+#!./parrot
+# Copyright (C) 2010, Parrot Foundation.
+# $Id$
+
+.include 'timer.pasm'
+
+.sub main
+    .include 'test_more.pir'
+
+    plan(7)
+    
+    $P0 = new 'Integer'
+    $P0 = 0
+    set_global 'A', $P0
+
+    $N0 = time
+    $N0 = $N0 + 0.25
+    
+    $P0 = get_global 'alarm2'
+    $N1 = $N0 + 0.04
+    make_alarm($N1, $P0)
+    
+    $P0 = get_global 'alarm1'
+    $N1 = $N0 + 0.02
+    make_alarm($N1, $P0)
+
+    $P0 = get_global 'alarm4'
+    $N1 = $N0 + 0.10
+    make_alarm($N1, $P0)
+
+    $P0 = get_global 'alarm3'
+    $N1 = $N0 + 0.09
+    make_alarm($N1, $P0)
+
+loop:
+    $P0 = get_global 'A'
+    $I0 = $P0
+    if $I0 == 4 goto done
+    goto loop
+
+done:
+    ok(1, "All alarms executed")
+
+    $N1 = $N0 + 0.10
+    $N0 = time
+
+    if $N0 >= $N1 goto good
+    ok(0, "Alarms actually waited")
+    .return()
+
+good:
+    ok(1, "Alarms actually waited")
+
+    $P1 = get_global 'alarm_finish'
+    $N0 = time
+    $N0 = $N0 + 0.1
+    
+    $P0 = new 'Float'
+    $P0 = $N0
+    set_global 'FT', $P0
+
+    make_alarm($N0, $P1)
+
+    sleep 5.0
+    ok(0, "Alarm/sleep interaction")
+.end
+
+.sub make_alarm
+    .param num when
+    .param pmc proc
+    
+    $P1 = new 'Alarm'
+    $P1[.PARROT_ALARM_TIME] = when 
+    $P1[.PARROT_ALARM_SUB]  = proc
+
+    $P1()
+.end
+
+.sub inc_A
+    $P0 = get_global 'A'
+    $P0 = $P0 + 1
+    set_global 'A', $P0
+    .return($P0)
+.end
+
+.sub alarm1
+    $P0 = inc_A()
+    
+    if $P0 == 1 goto a1_good
+    ok(0, "alarm 1")
+    .return()
+ 
+a1_good:
+    ok(1, "alarm 1")
+    .return()
+.end
+
+.sub alarm2
+    $P0 = inc_A()
+    
+    if $P0 == 2 goto a1_good
+    ok(0, "alarm 2")
+    .return()
+ 
+a1_good:
+    ok(1, "alarm 2")
+    .return()
+.end
+
+.sub alarm3
+    $P0 = inc_A()
+    
+    if $P0 == 3 goto a1_good
+    ok(0, "alarm 3")
+    .return()
+ 
+a1_good:
+    ok(1, "alarm 3")
+    .return()
+.end
+
+.sub alarm4
+    $P0 = inc_A()
+    
+    if $P0 == 4 goto a1_good
+    ok(0, "alarm 4")
+    .return()
+ 
+a1_good:
+    ok(1, "alarm 4")
+    .return()
+.end
+
+.sub alarm_finish
+    $N0 = time
+
+    $P0 = get_global 'FT'
+    $N1 = $P0
+    
+    if $N0 < $N1 goto fail
+    $N1 = $N1 + 0.5
+    if $N0 > $N1 goto fail
+
+    ok(1, "Alarm/sleep interaction")
+    exit 0
+
+fail:
+    ok(0, "Alarm/sleep interaction")
+.end


More information about the parrot-commits mailing list