l4-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

New task server patch


From: Matthieu Lemerre
Subject: New task server patch
Date: Sun, 17 Apr 2005 14:00:41 +0200
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux)

Hello,

Thanks to Marcus' advises, I've rewritten the patch a bit.

-I used a different strategy regarding utcb: I don't need to store
 neither the utcb fpage nor the kip one in the task server anymore.

Previously I used this parameter so as to calculate the utcb location,
but as Marcus said, sometimes the user would want to create a thread
at a specific utcb location, so this was wrong. I did this because it
disabled some checks after thread_control operations (so it was worth
doing it).

BTW I noticed a bug in current cvs regarding utcb location: two
threads are allocated with the same utcb location (I did not found it
with my first patch, since I did not have to provide the UTCB
parameter)

-On the locking strategy: We need a lock, either global or local to a
 group (which is a circular singly-linked list of tasks). I chose to
 use a lock local to a group.


 *Insertions require only a lock on the "parent" task, and on a task
  which is not yet inserted. So this operation is not involved in any
  deadlocking problems between tasks.  So taking the group lock does
  not seem necessary.  However, it has the following advantages:

      ** Upon task group deletion, it ensures that no new tasks could
         be created that the task group deletion operation would
         forget.

      ** If you don't do that, when you search for the parent of a
         task (upon deletion), you would have to check that the task
         you locked is still the parent (a task could have been
         inserted meanwhile).

  So it's worth also taking the lock for insertion.
 
 *Deletions require to unlock all task we might have potentially lock,
  acquire the task group lock, and then lock two tasks (for simple
  termination) or all (for task group termination by the proc server).
  You can then release the task group lock.

-I was planning to use a check flag in the task for preventing fork
 bombs, but hopefully Neal told me that we could just have to provide
 a task/thread allocation capability when calling task_threads_create.
 So that elegantly solve the fork bomb problem.


This patch relies on the wortel patch (which implements
wortel_space_control) (which I resubmit here) and on the capability
object revocation patch I submitted yesterday (for deallocating the
capability object in task_terminate).


Here's the resubmitted wortel patch:

2005-04-17  Matthieu Lemerre <address@hidden>

        * wortel.c (serve_bootstrap_requests): Implement reply to
        WORTEL_MSG_SPACE_CONTROL.


--- old-darcs/hurd-l4/wortel/wortel.c   2005-02-14 02:22:16.000000000 +0100
+++ new-darcs/hurd-l4/wortel/wortel.c   2005-03-31 03:11:08.000000000 +0200
@@ -1231,6 +1231,27 @@ serve_bootstrap_requests (void)
          l4_msg_load (msg);
          l4_reply (from);
        }
+      else if (label == WORTEL_MSG_SPACE_CONTROL)
+       {
+         if (l4_untyped_words (tag) != 5 || l4_typed_words (tag) != 0)
+           panic ("Invalid format of thread control msg");
+
+         l4_thread_id_t space = l4_msg_word (msg, 0);
+         l4_word_t control = l4_msg_word (msg, 1);
+         l4_fpage_t kip = l4_msg_word (msg, 2);
+         l4_fpage_t utcb = l4_msg_word (msg, 3);
+         l4_thread_id_t redirector = l4_msg_word (msg, 4);
+         l4_word_t ret;
+         l4_word_t old_control;
+
+         ret = l4_space_control (space, control, kip, utcb,
+                                 redirector, &old_control);
+         l4_msg_clear (msg);
+         l4_msg_append_word (msg, ret ? 0 : l4_error_code ());
+         l4_msg_append_word (msg, old_control);
+         l4_msg_load (msg);
+         l4_reply (from);
+       }
       else if (label == WORTEL_MSG_GET_TASK_CAP_REQUEST)
        {
          if (cur_task == (unsigned int) -1)

Here's my task server patch:

Changelog:

2005-04-17  Matthieu Lemerre  <address@hidden>

        * task-user.h
        (task_threads_create, task_threads_terminate, task_terminate)
        (task_create_empty, task_create_task, task_threads_alloc)
        (task_thread_alloc): New declarations.
        * task-user.c:
        (task_threads_create, task_threads_terminate, task_terminate): New
        functions (RPC client stubs.)
        (task_create_empty, task_create_task, task_threads_alloc)
        (task_thread_alloc): New functions (wrappers for the above).
        * deva.c (setup_threads): Use the new task interface.
        * ia32-cmain.c (cmain): Likewise.
        Added include to task-user.h.

for ruth:

2005-04-17  Matthieu Lemerre  <address@hidden>

        * task-user.h
        (task_threads_create, task_threads_terminate, task_terminate)
        (task_create_empty, task_create_task, task_threads_alloc)
        (task_thread_alloc): New declarations.
        * task-user.c:
        (task_threads_create, task_threads_terminate, task_terminate): New
        functions (RPC client stubs.)
        (task_create_empty, task_create_task, task_threads_alloc)
        (task_thread_alloc): New functions (wrappers for the above).
        * ruth.c (setup_threads): Use the new task interface.
        * ia32-cmain.c (cmain): Likewise.
        Added include to task-user.h.

For libl4:

2005-04-17  Matthieu Lemerre  <address@hidden>

        * l4/gnu/thread.h (l4_utcb): Added gnu interface for _L4_utcb.

For task:

2005-04-17  Matthieu Lemerre  <address@hidden>

        * task-class.c (CHECK_FLAG): New macro.
        (task_reinit): comment fix, bug fix (use of deallocated memory)
        (task_thread_alloc): Function removed
        (task_terminate, task_threads_create, task_threads_terminate): New
        functions.
        (task_demuxer): Now use the new RPCs.
        (task_alloc): Comment fix. Bug fix (task->nr_threads initialized
        at the wrong place). Initialize the group field.

        * task.h (struct task): Added group field.
        (struct task_group_common): New struct.
        (task_group_common_t): New type.
        (TASK_NEW_TASK): new value.


        
diff -rN -u -p old-darcs/hurd-l4/deva/deva.c new-darcs/hurd-l4/deva/deva.c
--- old-darcs/hurd-l4/deva/deva.c       2005-02-14 02:22:17.000000000 +0100
+++ new-darcs/hurd-l4/deva/deva.c       2005-03-31 02:38:39.000000000 +0200
@@ -154,66 +154,44 @@ setup_threads (void)
 {
   error_t err;
   l4_thread_id_t server_thread;
-  l4_thread_id_t tid;
+  l4_thread_id_t tid[4];
   l4_thread_id_t pager;
+  l4_word_t num_allocated_threads;
   pthread_t thread;
   struct hurd_startup_cap *task = &__hurd_startup_data->task;
 
   server_thread = l4_my_global_id ();
-  err = task_thread_alloc (task->server, task->cap_handle,
-                          (void *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                          + l4_utcb_size ()),
-                          &tid);
+  pager = l4_pager ();
+
+  /* We allocate 4 threads: one for the manager, 2 extra threads, and
+     one for irq handlers.  */
+  err = task_threads_alloc (task->server, task->cap_handle, 4, pager,
+                           l4_address (__hurd_startup_data->utcb_area)
+                           + 2 * l4_utcb_size (),
+                           &num_allocated_threads, tid);
   if (err)
     panic ("could not create main task thread: %i", err);
-
+  else if (num_allocated_threads != 4)
+    panic ("could create only %d threads on 4:", num_allocated_threads);
+  
   /* Switch threads.  We still need the current main thread as the
      server thread.  */
-  pager = l4_pager ();
-  switch_thread (server_thread, tid);
+  switch_thread (server_thread, tid[0]);
   l4_set_pager (pager);
 
   /* Create the main thread.  */
   err = pthread_create (&thread, 0, 0, 0);
   if (err)
-    panic ("could not create main thread: %i\n", err);
+    panic ("could not create main thread: %x\n", err);
 
   /* FIXME: This is unecessary as soon as we implement this properly
      in pthread (of course, within the task server, we will use an
      override to not actually make an RPC to ourselves.  */
 
   /* Now add the remaining extra threads to the pool.  */
-  err = task_thread_alloc (task->server, task->cap_handle,
-                          (void *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                           + 2 * l4_utcb_size ()),
-                          &tid);
-  if (err)
-    panic ("could not create first extra thread: %i", err);
-
-  pthread_pool_add_np (tid);
-
-  err = task_thread_alloc (task->server, task->cap_handle,
-                          (void *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                           + 3 * l4_utcb_size ()),
-                          &tid);
-  if (err)
-    panic ("could not create first extra thread: %i", err);
-
-  pthread_pool_add_np (tid);
-
-  /* One more for irq handlers (blech).  */
-  err = task_thread_alloc (task->server, task->cap_handle,
-                          (void *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                           + 4 * l4_utcb_size ()),
-                          &tid);
-  if (err)
-    panic ("could not create first extra thread: %i", err);
-
-  pthread_pool_add_np (tid);
+  pthread_pool_add_np (tid[1]);
+  pthread_pool_add_np (tid[2]);
+  pthread_pool_add_np (tid[3]);
 
   return server_thread;
 }
diff -rN -u -p old-darcs/hurd-l4/deva/ia32-cmain.c 
new-darcs/hurd-l4/deva/ia32-cmain.c
--- old-darcs/hurd-l4/deva/ia32-cmain.c 2005-02-14 02:22:17.000000000 +0100
+++ new-darcs/hurd-l4/deva/ia32-cmain.c 2005-03-31 02:34:41.000000000 +0200
@@ -31,6 +31,7 @@
 #include <l4/stubs-init.h>
 
 #include "deva.h"
+#include "task-user.h"
 
 #include <hurd/wortel.h>
 #include <hurd/startup.h>
@@ -62,8 +63,8 @@ cmain (void)
   l4_init_stubs ();
   err = task_thread_alloc (__hurd_startup_data->task.server,
                           __hurd_startup_data->task.cap_handle,
-                          /* We are the second thread.  */
-                          (void *)
+                          /* FIXME: this should be a PHYSMEM thread.  */
+                          l4_my_global_id (),
                           (l4_address (__hurd_startup_data->utcb_area)
                            + l4_utcb_size ()),
                           &pager_tid);
diff -rN -u -p old-darcs/hurd-l4/deva/task-user.c 
new-darcs/hurd-l4/deva/task-user.c
--- old-darcs/hurd-l4/deva/task-user.c  2005-02-14 02:22:17.000000000 +0100
+++ new-darcs/hurd-l4/deva/task-user.c  2005-03-31 20:24:58.000000000 +0200
@@ -1,6 +1,6 @@
 /* task-user.c - User stubs for task RPCs.
    Copyright (C) 2004 Free Software Foundation, Inc.
-   Written by Marcus Brinkmann.
+   Written by Marcus Brinkmann and Matthieu Lemerre.
 
    This file is part of the GNU Hurd.
 
@@ -32,11 +32,33 @@
 #define HURD_L4_ERROR_LABEL ((uint16_t) INT16_MIN)
 
 
-/* Allocate a new thread for the task TASK, and return its thread ID
-   in THREAD_ID.  */
+
+
+/* Request allocation of NUM threads.  Threads are created with pager
+   PAGER.  If FLAG | TASK_NEW, operation happens on a new task, whose
+   handle is returned in RETURN_HANDLE.  KIP and UTCB have to be
+   specified when the number of threads in a task goes from 0 to 1,
+   they will be used on the space_control operation that will happen
+   then.  UTCB_LOCATION is used to specify where to allocate the UTCB
+   for the threads; the NUM threads UTCBs will be allocated between
+   UTCB_LOCATION and UTCB_LOCATION + NUM * l4_utcb_size ().
+
+   After the call, RETURN_HANDLE contains the task handle where
+   threads were allocated, NUM_ALLOCATED_THREADS the number of
+   allocated threads, and ALLOCATED_THREAD_IDS is filled with the
+   allocated thread ids.  */
 error_t
-task_thread_alloc (l4_thread_id_t task_server, hurd_cap_handle_t task,
-                  void *utcb, l4_thread_id_t *thread_id)
+task_threads_create (l4_thread_id_t task_server,
+                    hurd_cap_handle_t task,
+                    l4_word_t num,
+                    l4_thread_id_t pager,
+                    l4_word_t utcb_location,
+                    l4_fpage_t kip,
+                    l4_fpage_t utcb,
+                    l4_word_t flag,
+                    hurd_cap_handle_t *return_handle,
+                    l4_word_t *num_allocated_threads,
+                    l4_word_t *allocated_threads_ids)
 {
   l4_msg_t msg;
   l4_msg_tag_t tag;
@@ -44,18 +66,169 @@ task_thread_alloc (l4_thread_id_t task_s
   l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
 
   l4_msg_clear (msg);
-  l4_set_msg_label (msg, 512 /* TASK_THREAD_ALLOC */);
+  l4_set_msg_label (msg, 512 /* TASK_THREADS_CREATE */);
   l4_msg_append_word (msg, task);
-  l4_msg_append_word (msg, (l4_word_t) utcb);
+  l4_msg_append_word (msg, num);
+  l4_msg_append_word (msg, pager);
+  l4_msg_append_word (msg, utcb_location);
+  l4_msg_append_word (msg, kip);
+  l4_msg_append_word (msg, utcb);
+  l4_msg_append_word (msg, flag);
   l4_msg_load (msg);
   tag = l4_call (task_server);
 
   l4_msg_store (tag, msg);
+  if (l4_msg_label (msg) == HURD_L4_ERROR_LABEL)
+    return l4_msg_word (msg, 0);
+
+  *return_handle = l4_msg_word (msg, 0);
+  *num_allocated_threads = l4_msg_word (msg, 1);
+
+  int i;
+  for (i = 0; i < *num_allocated_threads; i++)
+    allocated_threads_ids[i] = l4_msg_word (msg, i + 2);
+
+  return 0;
+}
+
+/* Request deallocation of NUM threads.  THREAD_IDS is an array
+   containing the thread ids of the threads to deallocate.  NUM can be
+   0, in which case THREAD_IDS is ignored and all threads are
+   deallocated (this also destroys the address space).  After the
+   call, NUM_DEALLOCATED_THREADS contains the number of deallocated
+   threads. If not all threads were deallocated, ERROR contains the
+   reason why the NUMth thread was not deallocated, else 0.  */
+error_t
+task_threads_terminate (l4_thread_id_t task_server,
+                       hurd_cap_handle_t task,
+                       l4_word_t num,
+                       l4_thread_id_t *thread_ids,
+                       l4_word_t *num_deallocated_threads,
+                       error_t *error)
+{
+  l4_msg_t msg;
+  l4_msg_tag_t tag;
+
+  l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
 
+  l4_msg_clear (msg);
+  l4_set_msg_label (msg, 513 /* TASK_THREADS_TERMINATE */);
+  l4_msg_append_word (msg, task);
+
+  int i;
+  for (i = 0; i < num; i++)
+    l4_msg_append_word (msg, thread_ids[i]);
+  
+  l4_msg_load (msg);
+  tag = l4_call (task_server);
+
+  l4_msg_store (tag, msg);
   if (l4_msg_label (msg) == HURD_L4_ERROR_LABEL)
     return l4_msg_word (msg, 0);
 
-  *thread_id = l4_msg_word (msg, 0);
+  *num_deallocated_threads = l4_msg_word (msg, 0);
+  *error = l4_msg_word (msg, 1);
+
+  return 0;
+}
+
+
+/* Terminate the task TASK, releasing all associated ressources
+   associated with it.  */
+error_t
+task_terminate (l4_thread_id_t task_server,
+               hurd_cap_handle_t task)
+                     
+{
+  l4_msg_t msg;
+  l4_msg_tag_t tag;
 
+  l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
+
+  l4_msg_clear (msg);
+  l4_set_msg_label (msg, 514 /* TASK_TERMINATE */);
+  l4_msg_append_word (msg, task);
+  l4_msg_load (msg);
+  tag = l4_call (task_server);
+
+  l4_msg_store (tag, msg);
+  
+  if (l4_msg_label (msg) == HURD_L4_ERROR_LABEL)
+    return l4_msg_word (msg, 0);
+
+  return 0;
+}
+
+
+
+/* Create an empty task control capability.  Returns the task handle
+   in NEW_TASK.  */
+error_t
+task_create_empty (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   hurd_cap_handle_t *new_task)
+{
+  l4_word_t num_allocated_threads;
+  return task_threads_create (task_server, task, 0, l4_nilthread, 0,
+                             0, 0, TASK_NEW_TASK, new_task,
+                             &num_allocated_threads, NULL);
+}
+
+/* Create a new task with one thread.  Returns the task handle in
+   NEW_TASK, and the thread id in THREAD_ID.  */
+error_t
+task_create_task (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                 l4_thread_id_t pager,
+                 l4_fpage_t kip,
+                 l4_fpage_t utcb,
+                  hurd_cap_handle_t *new_task,
+                  l4_thread_id_t *thread_id)
+{
+  l4_word_t num_allocated_threads;
+  return task_threads_create (task_server, task, 1, pager, l4_address (utcb),
+                             kip, utcb, TASK_NEW_TASK, new_task,
+                             &num_allocated_threads, thread_id);
+}
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager and UTCB_LOCATION for allocating the UTCB.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_threads_alloc (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   l4_word_t num,
+                   l4_word_t pager,
+                   l4_word_t utcb_location,
+                   l4_word_t *num_allocated_threads,
+                   l4_thread_id_t *allocated_threads_ids)
+{
+  hurd_cap_handle_t new_task;
+  return task_threads_create (task_server, task, num, pager,
+                             utcb_location, 0, 0, 0, &new_task,
+                             num_allocated_threads, allocated_threads_ids);
+}
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager, and allocating the UTCB at UTCB_LOCATION.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_thread_alloc (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                  l4_word_t pager,
+                  l4_word_t utcb_location,
+                  l4_thread_id_t *thread_id)
+{
+  l4_word_t num_allocated_threads;
+  error_t err;
+  err = task_threads_alloc (task_server, task, 1, pager, utcb_location,
+                           &num_allocated_threads, thread_id);
+  if (err)
+    return err;
+  if (num_allocated_threads != 1)
+    return EAGAIN;
   return 0;
 }
diff -rN -u -p old-darcs/hurd-l4/deva/task-user.h 
new-darcs/hurd-l4/deva/task-user.h
--- old-darcs/hurd-l4/deva/task-user.h  2005-02-14 02:22:17.000000000 +0100
+++ new-darcs/hurd-l4/deva/task-user.h  2005-03-31 01:39:44.000000000 +0200
@@ -1,6 +1,6 @@
 /* task-user.h - User stub interfaces for task RPCs.
    Copyright (C) 2004 Free Software Foundation, Inc.
-   Written by Marcus Brinkmann.
+   Written by Marcus Brinkmann and Matthieu Lemerre.
 
    This file is part of the GNU Hurd.
 
@@ -29,9 +29,105 @@
 #include <hurd/types.h>
 
 
-/* Allocate a new thread for the task TASK, and return its thread ID
-   in THREAD_ID.  */
-error_t task_thread_alloc (l4_thread_id_t task_server, hurd_cap_handle_t task,
-                          void *utcb, l4_thread_id_t *thread_id);
+
+
+/* Request allocation of NUM threads.  Threads are created with pager
+   PAGER.  If FLAG | TASK_NEW, operation happens on a new task, whose
+   handle is returned in RETURN_HANDLE.  KIP and UTCB have to be
+   specified when the number of threads in a task goes from 0 to 1,
+   they will be used on the space_control operation that will happen
+   then.  UTCB_LOCATION is used to specify where to allocate the UTCB
+   for the threads; the NUM threads UTCBs will be allocated between
+   UTCB_LOCATION and UTCB_LOCATION + NUM * l4_utcb_size ().
+
+   After the call, RETURN_HANDLE contains the task handle where
+   threads were allocated, NUM_ALLOCATED_THREADS the number of
+   allocated threads, and ALLOCATED_THREAD_IDS is filled with the
+   allocated thread ids.  */
+error_t
+task_threads_create (l4_thread_id_t task_server,
+                    hurd_cap_handle_t task,
+                    l4_word_t num,
+                    l4_thread_id_t pager,
+                    l4_word_t utcb_location,
+                    l4_fpage_t kip,
+                    l4_fpage_t utcb,
+                    l4_word_t flag,
+                    hurd_cap_handle_t *return_handle,
+                    l4_word_t *num_allocated_threads,
+                    l4_word_t *allocated_threads_ids);
+
+
+/* Request deallocation of NUM threads.  THREAD_IDS is an array
+   containing the thread ids of the threads to deallocate.  NUM can be
+   0, in which case THREAD_IDS is ignored and all threads are
+   deallocated (this also destroys the address space).  After the
+   call, NUM_DEALLOCATED_THREADS contains the number of deallocated
+   threads. If not all threads were deallocated, ERROR contains the
+   reason why the NUMth thread was not deallocated, else 0.  */
+error_t
+task_threads_terminate (l4_thread_id_t task_server,
+                       hurd_cap_handle_t task,
+                       l4_word_t num,
+                       l4_thread_id_t *thread_ids,
+                       l4_word_t *num_deallocated_threads,
+                       error_t *error);
+
+/* Terminate the task TASK, releasing all associated ressources
+   associated with it.  */
+error_t
+task_terminate (l4_thread_id_t task_server,
+               hurd_cap_handle_t task);
+
+
+
+/* Create an empty task control capability.  Returns the task handle
+   in NEW_TASK.  */
+error_t
+task_create_empty (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   hurd_cap_handle_t *new_task);
+
+
+/* Create a new task with one thread.  Returns the task handle in
+   NEW_TASK, and the thread id in THREAD_ID.  */
+error_t
+task_create_task (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                 l4_thread_id_t pager,
+                 l4_fpage_t kip,
+                 l4_fpage_t utcb,
+                  hurd_cap_handle_t *new_task,
+                  l4_thread_id_t *thread_id);
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager and UTCB_LOCATION for allocating the UTCB.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_threads_alloc (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   l4_word_t num,
+                   l4_word_t pager,
+                   l4_word_t utcb_location,
+                   l4_word_t *num_allocated_threads,
+                   l4_thread_id_t *allocated_threads_ids);
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager, and allocating the UTCB at UTCB_LOCATION.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_thread_alloc (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                  l4_word_t pager,
+                  l4_word_t utcb_location,
+                  l4_thread_id_t *thread_id);
+
+enum {
+  TASK_NEW_TASK = 1, /* Allocate the threads in a new created task.  */
+};
 
 #endif /* HURD_TASK_USER_H */
diff -rN -u -p old-darcs/hurd-l4/ruth/ia32-cmain.c 
new-darcs/hurd-l4/ruth/ia32-cmain.c
--- old-darcs/hurd-l4/ruth/ia32-cmain.c 2005-02-14 02:22:16.000000000 +0100
+++ new-darcs/hurd-l4/ruth/ia32-cmain.c 2005-03-31 02:40:11.000000000 +0200
@@ -31,6 +31,7 @@
 #include <l4/stubs-init.h>
 
 #include "ruth.h"
+#include "task-user.h"
 
 #include <hurd/wortel.h>
 #include <hurd/startup.h>
@@ -62,8 +63,8 @@ cmain (void)
   l4_init_stubs ();
   err = task_thread_alloc (__hurd_startup_data->task.server,
                           __hurd_startup_data->task.cap_handle,
-                          /* We are the second thread.  */
-                          (void *)
+                          /* FIXME: this should be a PHYSMEM thread.  */
+                          l4_my_global_id (),
                           (l4_address (__hurd_startup_data->utcb_area)
                            + l4_utcb_size ()),
                           &pager_tid);
diff -rN -u -p old-darcs/hurd-l4/ruth/ruth.c new-darcs/hurd-l4/ruth/ruth.c
--- old-darcs/hurd-l4/ruth/ruth.c       2005-02-14 02:22:16.000000000 +0100
+++ new-darcs/hurd-l4/ruth/ruth.c       2005-04-13 20:46:13.000000000 +0200
@@ -81,24 +81,28 @@ setup_threads (void)
 {
   error_t err;
   l4_thread_id_t server_thread;
-  l4_thread_id_t tid;
+  l4_thread_id_t tid[3];
   l4_thread_id_t pager;
+  l4_word_t num_allocated_threads;
   pthread_t thread;
   struct hurd_startup_cap *task = &__hurd_startup_data->task;
 
   server_thread = l4_my_global_id ();
-  err = task_thread_alloc (task->server, task->cap_handle,
-                          (void *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                          + l4_utcb_size ()),
-                          &tid);
+  pager = l4_pager ();
+
+  /* We allocate 3 threads: one for the manager and 2 extra threads.  */
+    err = task_threads_alloc (task->server, task->cap_handle, 3, pager,
+                             l4_address (__hurd_startup_data->utcb_area)
+                             + 2 * l4_utcb_size (),
+                             &num_allocated_threads, tid);
   if (err)
     panic ("could not create main task thread: %i", err);
-
+  else if (num_allocated_threads != 3)
+    panic ("could create only %d threads on 3:", num_allocated_threads);
+  
   /* Switch threads.  We still need the current main thread as the
      server thread.  */
-  pager = l4_pager ();
-  switch_thread (server_thread, tid);
+  switch_thread (server_thread, tid[0]);
   l4_set_pager (pager);
 
   /* Create the main thread.  */
@@ -111,29 +115,11 @@ setup_threads (void)
      override to not actually make an RPC to ourselves.  */
 
   /* Now add the remaining extra threads to the pool.  */
-  err = task_thread_alloc (task->server, task->cap_handle,
-                          (void *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                           + 2 * l4_utcb_size ()),
-                          &tid);
-  if (err)
-    panic ("could not create first extra thread: %i", err);
-
-  pthread_pool_add_np (tid);
-
-  err = task_thread_alloc (task->server, task->cap_handle,
-                          (void *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                           + 3 * l4_utcb_size ()),
-                          &tid);
-  if (err)
-    panic ("could not create first extra thread: %i", err);
-
-  pthread_pool_add_np (tid);
+  pthread_pool_add_np (tid[1]);
+  pthread_pool_add_np (tid[2]);
 
   return server_thread;
 }
-
 
 #if 0
 void *
diff -rN -u -p old-darcs/hurd-l4/ruth/task-user.c 
new-darcs/hurd-l4/ruth/task-user.c
--- old-darcs/hurd-l4/ruth/task-user.c  2005-02-14 02:22:16.000000000 +0100
+++ new-darcs/hurd-l4/ruth/task-user.c  2005-03-31 20:25:08.000000000 +0200
@@ -1,6 +1,6 @@
 /* task-user.c - User stubs for task RPCs.
    Copyright (C) 2004 Free Software Foundation, Inc.
-   Written by Marcus Brinkmann.
+   Written by Marcus Brinkmann and Matthieu Lemerre.
 
    This file is part of the GNU Hurd.
 
@@ -32,11 +32,33 @@
 #define HURD_L4_ERROR_LABEL ((uint16_t) INT16_MIN)
 
 
-/* Allocate a new thread for the task TASK, and return its thread ID
-   in THREAD_ID.  */
+
+
+/* Request allocation of NUM threads.  Threads are created with pager
+   PAGER.  If FLAG | TASK_NEW, operation happens on a new task, whose
+   handle is returned in RETURN_HANDLE.  KIP and UTCB have to be
+   specified when the number of threads in a task goes from 0 to 1,
+   they will be used on the space_control operation that will happen
+   then.  UTCB_LOCATION is used to specify where to allocate the UTCB
+   for the threads; the NUM threads UTCBs will be allocated between
+   UTCB_LOCATION and UTCB_LOCATION + NUM * l4_utcb_size ().
+
+   After the call, RETURN_HANDLE contains the task handle where
+   threads were allocated, NUM_ALLOCATED_THREADS the number of
+   allocated threads, and ALLOCATED_THREAD_IDS is filled with the
+   allocated thread ids.  */
 error_t
-task_thread_alloc (l4_thread_id_t task_server, hurd_cap_handle_t task,
-                  void *utcb, l4_thread_id_t *thread_id)
+task_threads_create (l4_thread_id_t task_server,
+                    hurd_cap_handle_t task,
+                    l4_word_t num,
+                    l4_thread_id_t pager,
+                    l4_word_t utcb_location,
+                    l4_fpage_t kip,
+                    l4_fpage_t utcb,
+                    l4_word_t flag,
+                    hurd_cap_handle_t *return_handle,
+                    l4_word_t *num_allocated_threads,
+                    l4_word_t *allocated_threads_ids)
 {
   l4_msg_t msg;
   l4_msg_tag_t tag;
@@ -44,18 +66,169 @@ task_thread_alloc (l4_thread_id_t task_s
   l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
 
   l4_msg_clear (msg);
-  l4_set_msg_label (msg, 512 /* TASK_THREAD_ALLOC */);
+  l4_set_msg_label (msg, 512 /* TASK_THREADS_CREATE */);
   l4_msg_append_word (msg, task);
-  l4_msg_append_word (msg, (l4_word_t) utcb);
+  l4_msg_append_word (msg, num);
+  l4_msg_append_word (msg, pager);
+  l4_msg_append_word (msg, utcb_location);
+  l4_msg_append_word (msg, kip);
+  l4_msg_append_word (msg, utcb);
+  l4_msg_append_word (msg, flag);
   l4_msg_load (msg);
   tag = l4_call (task_server);
 
   l4_msg_store (tag, msg);
+  if (l4_msg_label (msg) == HURD_L4_ERROR_LABEL)
+    return l4_msg_word (msg, 0);
+
+  *return_handle = l4_msg_word (msg, 0);
+  *num_allocated_threads = l4_msg_word (msg, 1);
+
+  int i;
+  for (i = 0; i < *num_allocated_threads; i++)
+    allocated_threads_ids[i] = l4_msg_word (msg, i + 2);
+
+  return 0;
+}
+
+/* Request deallocation of NUM threads.  THREAD_IDS is an array
+   containing the thread ids of the threads to deallocate.  NUM can be
+   0, in which case THREAD_IDS is ignored and all threads are
+   deallocated (this also destroys the address space).  After the
+   call, NUM_DEALLOCATED_THREADS contains the number of deallocated
+   threads. If not all threads were deallocated, ERROR contains the
+   reason why the NUMth thread was not deallocated, else 0.  */
+error_t
+task_threads_terminate (l4_thread_id_t task_server,
+                       hurd_cap_handle_t task,
+                       l4_word_t num,
+                       l4_thread_id_t *thread_ids,
+                       l4_word_t *num_deallocated_threads,
+                       error_t *error)
+{
+  l4_msg_t msg;
+  l4_msg_tag_t tag;
+
+  l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
 
+  l4_msg_clear (msg);
+  l4_set_msg_label (msg, 513 /* TASK_THREADS_TERMINATE */);
+  l4_msg_append_word (msg, task);
+
+  int i;
+  for (i = 0; i < num; i++)
+    l4_msg_append_word (msg, thread_ids[i]);
+  
+  l4_msg_load (msg);
+  tag = l4_call (task_server);
+
+  l4_msg_store (tag, msg);
   if (l4_msg_label (msg) == HURD_L4_ERROR_LABEL)
     return l4_msg_word (msg, 0);
 
-  *thread_id = l4_msg_word (msg, 0);
+  *num_deallocated_threads = l4_msg_word (msg, 0);
+  *error = l4_msg_word (msg, 1);
+
+  return 0;
+}
+
+
+/* Terminate the task TASK, releasing all associated ressources
+   associated with it.  */
+error_t
+task_terminate (l4_thread_id_t task_server,
+               hurd_cap_handle_t task)
+                     
+{
+  l4_msg_t msg;
+  l4_msg_tag_t tag;
 
+  l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
+
+  l4_msg_clear (msg);
+  l4_set_msg_label (msg, 514 /* TASK_TERMINATE */);
+  l4_msg_append_word (msg, task);
+  l4_msg_load (msg);
+  tag = l4_call (task_server);
+
+  l4_msg_store (tag, msg);
+  
+  if (l4_msg_label (msg) == HURD_L4_ERROR_LABEL)
+    return l4_msg_word (msg, 0);
+
+  return 0;
+}
+
+
+
+/* Create an empty task control capability.  Returns the task handle
+   in NEW_TASK.  */
+error_t
+task_create_empty (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   hurd_cap_handle_t *new_task)
+{
+  l4_word_t num_allocated_threads;
+  return task_threads_create (task_server, task, 0, l4_nilthread, 0,
+                             0, 0, TASK_NEW_TASK, new_task,
+                             &num_allocated_threads, NULL);
+}
+
+/* Create a new task with one thread.  Returns the task handle in
+   NEW_TASK, and the thread id in THREAD_ID.  */
+error_t
+task_create_task (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                 l4_thread_id_t pager,
+                 l4_fpage_t kip,
+                 l4_fpage_t utcb,
+                  hurd_cap_handle_t *new_task,
+                  l4_thread_id_t *thread_id)
+{
+  l4_word_t num_allocated_threads;
+  return task_threads_create (task_server, task, 1, pager, l4_address (utcb),
+                             kip, utcb, TASK_NEW_TASK, new_task,
+                             &num_allocated_threads, thread_id);
+}
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager and UTCB_LOCATION for allocating the UTCB.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_threads_alloc (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   l4_word_t num,
+                   l4_word_t pager,
+                   l4_word_t utcb_location,
+                   l4_word_t *num_allocated_threads,
+                   l4_thread_id_t *allocated_threads_ids)
+{
+  hurd_cap_handle_t new_task;
+  return task_threads_create (task_server, task, num, pager,
+                             utcb_location, 0, 0, 0, &new_task,
+                             num_allocated_threads, allocated_threads_ids);
+}
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager, and allocating the UTCB at UTCB_LOCATION.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_thread_alloc (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                  l4_word_t pager,
+                  l4_word_t utcb_location,
+                  l4_thread_id_t *thread_id)
+{
+  l4_word_t num_allocated_threads;
+  error_t err;
+  err = task_threads_alloc (task_server, task, 1, pager, utcb_location,
+                           &num_allocated_threads, thread_id);
+  if (err)
+    return err;
+  if (num_allocated_threads != 1)
+    return EAGAIN;
   return 0;
 }
diff -rN -u -p old-darcs/hurd-l4/ruth/task-user.h 
new-darcs/hurd-l4/ruth/task-user.h
--- old-darcs/hurd-l4/ruth/task-user.h  2005-02-14 02:22:16.000000000 +0100
+++ new-darcs/hurd-l4/ruth/task-user.h  2005-03-31 02:39:56.000000000 +0200
@@ -1,6 +1,6 @@
 /* task-user.h - User stub interfaces for task RPCs.
    Copyright (C) 2004 Free Software Foundation, Inc.
-   Written by Marcus Brinkmann.
+   Written by Marcus Brinkmann and Matthieu Lemerre.
 
    This file is part of the GNU Hurd.
 
@@ -29,9 +29,105 @@
 #include <hurd/types.h>
 
 
-/* Allocate a new thread for the task TASK, and return its thread ID
-   in THREAD_ID.  */
-error_t task_thread_alloc (l4_thread_id_t task_server, hurd_cap_handle_t task,
-                          void *utcb, l4_thread_id_t *thread_id);
+
+
+/* Request allocation of NUM threads.  Threads are created with pager
+   PAGER.  If FLAG | TASK_NEW, operation happens on a new task, whose
+   handle is returned in RETURN_HANDLE.  KIP and UTCB have to be
+   specified when the number of threads in a task goes from 0 to 1,
+   they will be used on the space_control operation that will happen
+   then.  UTCB_LOCATION is used to specify where to allocate the UTCB
+   for the threads; the NUM threads UTCBs will be allocated between
+   UTCB_LOCATION and UTCB_LOCATION + NUM * l4_utcb_size ().
+
+   After the call, RETURN_HANDLE contains the task handle where
+   threads were allocated, NUM_ALLOCATED_THREADS the number of
+   allocated threads, and ALLOCATED_THREAD_IDS is filled with the
+   allocated thread ids.  */
+error_t
+task_threads_create (l4_thread_id_t task_server,
+                    hurd_cap_handle_t task,
+                    l4_word_t num,
+                    l4_thread_id_t pager,
+                    l4_word_t utcb_location,
+                    l4_fpage_t kip,
+                    l4_fpage_t utcb,
+                    l4_word_t flag,
+                    hurd_cap_handle_t *return_handle,
+                    l4_word_t *num_allocated_threads,
+                    l4_word_t *allocated_threads_ids);
+
+
+/* Request deallocation of NUM threads.  THREAD_IDS is an array
+   containing the thread ids of the threads to deallocate.  NUM can be
+   0, in which case THREAD_IDS is ignored and all threads are
+   deallocated (this also destroys the address space).  After the
+   call, NUM_DEALLOCATED_THREADS contains the number of deallocated
+   threads. If not all threads were deallocated, ERROR contains the
+   reason why the NUMth thread was not deallocated, else 0.  */
+error_t
+task_threads_terminate (l4_thread_id_t task_server,
+                       hurd_cap_handle_t task,
+                       l4_word_t num,
+                       l4_thread_id_t *thread_ids,
+                       l4_word_t *num_deallocated_threads,
+                       error_t *error);
+
+/* Terminate the task TASK, releasing all associated ressources
+   associated with it.  */
+error_t
+task_terminate (l4_thread_id_t task_server,
+               hurd_cap_handle_t task);
+
+
+
+/* Create an empty task control capability.  Returns the task handle
+   in NEW_TASK.  */
+error_t
+task_create_empty (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   hurd_cap_handle_t *new_task);
+
+
+/* Create a new task with one thread.  Returns the task handle in
+   NEW_TASK, and the thread id in THREAD_ID.  */
+error_t
+task_create_task (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                 l4_thread_id_t pager,
+                 l4_fpage_t kip,
+                 l4_fpage_t utcb,
+                  hurd_cap_handle_t *new_task,
+                  l4_thread_id_t *thread_id);
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager and UTCB_LOCATION for allocating the UTCB.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_threads_alloc (l4_thread_id_t task_server,
+                   hurd_cap_handle_t task,
+                   l4_word_t num,
+                   l4_word_t pager,
+                   l4_word_t utcb_location,
+                   l4_word_t *num_allocated_threads,
+                   l4_thread_id_t *allocated_threads_ids);
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager, and allocating the UTCB at UTCB_LOCATION.  Number of
+   allocated threads is returned in NUM_ALLOCATED_THREADS,
+   ALLOCATED_THREADS_IDS is filled with the thread ids.  */
+error_t
+task_thread_alloc (l4_thread_id_t task_server,
+                  hurd_cap_handle_t task,
+                  l4_word_t pager,
+                  l4_word_t utcb_location,
+                  l4_thread_id_t *thread_id);
+
+enum {
+  TASK_NEW_TASK = 1, /* Allocate the threads in a new created task.  */
+};
 
 #endif /* HURD_TASK_USER_H */
diff -rN -u -p old-darcs/hurd-l4/task/task-class.c 
new-darcs/hurd-l4/task/task-class.c
--- old-darcs/hurd-l4/task/task-class.c 2005-02-14 02:22:16.000000000 +0100
+++ new-darcs/hurd-l4/task/task-class.c 2005-04-17 01:07:24.000000000 +0200
@@ -1,6 +1,6 @@
 /* task-class.c - Task class for the task server.
    Copyright (C) 2003, 2004 Free Software Foundation, Inc.
-   Written by Marcus Brinkmann.
+   Written by Marcus Brinkmann and Matthieu Lemerre.
 
    This file is part of the GNU Hurd.
 
@@ -31,82 +31,380 @@
 
 #include "task.h"
 
+/* Check if the bit BIT in FLAGS is set.  */
+#define CHECK_FLAG(flags,bit)  ((flags) & (1 << (bit)))
+
+
+static struct hurd_cap_class task_class;
+
+
 
 static void
 task_reinit (hurd_cap_class_t cap_class, hurd_cap_obj_t obj)
 {
   task_t task = hurd_cap_obj_to_user (task_t, obj);
   thread_t thread;
+  error_t err;
+
+  debug ("task %x reinitialized\n", task->task_id);
 
   thread = task->threads;
 
-  /* Destroy all threads.  */
+  /* Destroy all threads, thus also destroying the address space.  */
   while (thread)
     {
-      /* FIXME: We are ignoring an error here.  */
-      wortel_thread_control (thread->thread_id, l4_nilthread, l4_nilthread,
-                            l4_nilthread, (void *) -1);
+      err = wortel_thread_control (thread->thread_id, l4_nilthread,
+                                  l4_nilthread, l4_nilthread, (void *) -1);
+      assert (err == 0);
+      thread_t thread_next = thread->next;
       thread_dealloc (thread);
-      thread = thread->next;
+      thread = thread_next;
     }
 
+  task->nr_threads = 0;
+
   /* FIXME: Return the task ID to the list of free task IDs for future
      allocation.  */
 }
 
 
+
+/* Terminate the current task and destroy the capability object.  */
 error_t
-task_thread_alloc (hurd_cap_rpc_context_t ctx)
+task_terminate (hurd_cap_rpc_context_t ctx)
 {
   task_t task = hurd_cap_obj_to_user (task_t, ctx->obj);
+  hurd_cap_obj_unlock (ctx->obj);
+
+  if (task->group.next != task)
+    {
+      /* Find the previous task node in the circular linked list.  */
+      task_t previous = task->group.next;
+      hurd_cap_obj_t previous_obj;
+      
+      pthread_mutex_lock (task->group.lock);
+      do 
+       {
+         previous = previous->group.next;
+       } while (previous->group.next != task);
+  
+      previous_obj = hurd_cap_obj_from_user (task_t, previous);
+
+      pthread_mutex_lock (&previous_obj->lock);
+
+      /* No insertion/deletion should have happened inbetween.  */
+      assert (previous->group.next == task);
+      
+      previous->group.next = task->group.next;
+      pthread_mutex_unlock (&previous_obj->lock);
+      pthread_mutex_unlock (task->group.lock);
+    }
+  else
+    {
+      /* It was the only task left in the group, so we free the mutex.  */
+      pthread_mutex_destroy (task->group.lock);
+      free (task->group.common);
+    }
+
+  /* FIXME : Send task death notifications to each task which
+     holds a task info capability.  */
+
+  hurd_cap_obj_destroy (ctx->obj);
+          
+  /* FIXME: Return the task id to the list of free task ids.  */
+  return 0;
+}
+
+/* Create new tasks and threads.  */
+error_t
+task_threads_create (hurd_cap_rpc_context_t ctx)
+{
+  l4_word_t num_requested_threads = l4_msg_word (ctx->msg, 1);
+  l4_thread_id_t pager = l4_msg_word (ctx->msg, 2);
+  l4_word_t utcb_location = l4_msg_word (ctx->msg, 3);
+  l4_fpage_t kip = l4_msg_word (ctx->msg, 4);
+  l4_fpage_t utcb = l4_msg_word (ctx->msg, 5);
+  l4_word_t flag = l4_msg_word (ctx->msg, 6);
+
   error_t err;
-  thread_t thread;
-  void *utcb;
-  l4_word_t result;
+  hurd_cap_obj_t obj;
+  task_t task, parent_task = hurd_cap_obj_to_user (task_t, ctx->obj);
+  hurd_cap_handle_t handle;
 
-  /* Does not need to be checked.  */
-  utcb = (void *) l4_msg_word (ctx->msg, 1);
 
-  err = thread_alloc (&thread);
-  if (err)
-    return err;
+  /* We can only send up to L4_NUM_MRS - 4 thread ids in message
+     registers (we have to send other words too).  */
+  if (num_requested_threads >= (L4_NUM_MRS - 4))
+    num_requested_threads = (L4_NUM_MRS - 4);
+  
+  if (CHECK_FLAG (flag, TASK_NEW_TASK))
+    {
+      /* Creation of a new task is requested.  */
+      err = hurd_cap_class_alloc (&task_class, &obj);
+      if (err)
+       return err;
+      task = hurd_cap_obj_to_user (task_t, obj);
+      
+      hurd_task_id_t task_id;
+      err = task_id_add (task, &task_id);
+      if (err)
+       {
+         hurd_cap_obj_drop (obj);
+         return err;
+       }
+
+      debug ("Allocating new task with task id %d\n", task_id);
+      
+      task->nr_threads = 0;
+      task->group.next = parent_task->group.next;
+      task->group.common = parent_task->group.common;
+    }
+  else
+    {
+      task = parent_task;
+      handle = l4_msg_word (ctx->msg, 0);
+    }
+
+  /* Allocate a thread in R_THREAD, using ADDRESS_SPACE as the address
+     space thread id (if ADDRESS_SPACE is l4_nilthread, a new address
+     space is created).  */
+  error_t task_alloc_thread (thread_t *r_thread, l4_thread_id_t address_space)
+  {
+    thread_t thread;
+    err = thread_alloc (&thread);
+    if (err)
+      return err;
+    
+    thread->thread_id = l4_global_id (l4_thread_no (thread->thread_id),
+                                     task->task_id);
+
+    if (address_space != l4_nilthread)
+      {
+       err = wortel_thread_control (thread->thread_id,
+                                    address_space,
+                                    l4_myself (),
+                                    pager,
+                                    (void *) utcb_location);
+      }
+    else
+      {
+       /* When creating a new address space, the thread must be created
+          inactive.  */
+       err = wortel_thread_control (thread->thread_id,
+                                    thread->thread_id,
+                                    l4_myself (),
+                                    0,
+                                    (void *) utcb_location);
+      }
+    
+    assert (err == 0);
+    utcb_location += l4_utcb_size ();
+
+    thread->next = task->threads;
+    task->threads = thread;
+    *r_thread = thread;
+    return 0;
+  }
 
-  thread->thread_id = l4_global_id (l4_thread_no (thread->thread_id),
-                                   task->task_id);
+  l4_word_t allocated_threads = 0;
 
-  /* Put the thread into the task as an active thread.  FIXME:
-     Scheduler.  */
-  result = wortel_thread_control (thread->thread_id, task->threads->thread_id,
-                              l4_myself (), thread->thread_id, utcb);
-  if (result)
+  if (!task->nr_threads && num_requested_threads)
     {
-      /* FIXME: Convert error codes in wortel.h.  */
-      thread_dealloc (thread);
-      return EINVAL;
+      /* We need to create the first thread of the task.  */
+      if (utcb_location < l4_address (utcb) \
+         || utcb_location > l4_address (utcb) + l4_size (utcb) - l4_utcb_size 
())
+       {
+         err = EINVAL;
+         goto no_thread;
+       }
+      
+      thread_t thread;
+      err = task_alloc_thread (&thread, l4_nilthread);
+      if (err)
+       goto no_thread;
+
+      l4_word_t control;
+      err = wortel_space_control (thread->thread_id, 0,
+                                 kip,
+                                 utcb,
+                                 l4_anythread, &control);
+      if (err)
+       {
+         /* Destroy the task.  This will clean the thread.  */
+         hurd_cap_obj_lock (obj);
+         hurd_cap_obj_drop (obj);
+         return EINVAL;
+       }
+
+      /* Set the pager of the first thread (thus activating it).  */
+      err = wortel_thread_control (thread->thread_id,
+                                  thread->thread_id,
+                                  l4_nilthread,
+                                  pager,
+                                  (void *) -1);
+      assert (err == 0);
+      num_requested_threads --;
+      allocated_threads = 1;
+    }
+
+  /* Allocates new threads for that task.  */
+
+  int i;
+  for (i = 0; i < num_requested_threads; i++)
+    {
+      thread_t thread;
+      err = task_alloc_thread (&thread, task->threads->thread_id);
+      if (err)
+       break;
     }
+  
+  allocated_threads += i;
+  task->nr_threads += allocated_threads;
+
+ no_thread:
+  
+  if (CHECK_FLAG (flag, TASK_NEW_TASK))
+    {
+      hurd_cap_obj_unlock (obj);
+
+      /* Insert the created task into the task group.  */
+      err = hurd_cap_bucket_inject (ctx->bucket, obj, ctx->sender, &handle);
+      if (err)
+       {
+         hurd_cap_obj_lock (obj);
+         hurd_cap_obj_drop (obj);
+         return err;
+       }
 
-  thread->next = task->threads;
-  task->threads = thread;
-  task->nr_threads++;
+      /* OBJ now has 2 references : one because it was created, and
+        one when it was injected in the bucket.  Drop the extra
+        reference.  */
+      hurd_cap_obj_rele (obj);
+
+      hurd_cap_obj_unlock (ctx->obj);
+      pthread_mutex_lock (parent_task->group.lock);
+      parent_task->group.next = task;
+      pthread_mutex_unlock (parent_task->group.lock);
+      hurd_cap_obj_lock (ctx->obj);
+    }
+     
+  /* Constructs the return message.  */
 
-  /* Prepare reply message.  */
   l4_msg_clear (ctx->msg);
-  l4_msg_append_word (ctx->msg, thread->thread_id);
+  l4_msg_append_word (ctx->msg, handle);
+  l4_msg_append_word (ctx->msg, allocated_threads);
+
+  thread_t thread;
+  thread = task->threads;
+  for (i = 0; i < allocated_threads; i++)
+    {
+      l4_msg_append_word (ctx->msg, thread->thread_id);
+      thread = thread->next;
+    }
+
+  debug ("%d thread(s) was (were) allocated for task %d\n",
+        allocated_threads,
+        task->task_id);
 
+  
   return 0;
 }
 
 
 error_t
+task_threads_terminate (hurd_cap_rpc_context_t ctx)
+{
+  error_t err = 0;
+  task_t task = hurd_cap_obj_to_user (task_t, ctx->obj);
+  l4_word_t num_to_deallocate;
+  l4_word_t num_deallocated = 0;
+  num_to_deallocate = l4_untyped_words (l4_msg_msg_tag (ctx->msg)) - 1;
+  
+  if (num_to_deallocate == 0)
+    {
+      /* 0 means deallocate every thread in the task.  */
+      
+      debug ("Deallocating every thread in task %d\n", task->task_id);
+
+      thread_t thread = task->threads;
+      while (thread)
+       {
+         err = wortel_thread_control (thread->thread_id, l4_nilthread,
+                                      l4_nilthread, l4_nilthread, (void *) -1);
+         assert (err == 0);
+         thread_dealloc (thread);
+         thread = thread->next;
+         num_deallocated++;
+       }
+    }
+  else
+    {
+      debug ("Trying to deallocate %d thread(s) in task %d\n",
+            num_to_deallocate, task->task_id);
+      int i;
+      for (i = 0; i < num_to_deallocate; i++)
+       {
+         l4_thread_id_t thread_id = l4_msg_word (ctx->msg, i + 1);
+         thread_t thread = task->threads;
+         thread_t *next_thread = &task->threads;
+         
+         while (thread)
+           {
+             if (thread_id == thread->thread_id)
+               {
+                 /* Destroy the thread.  */
+                 err = wortel_thread_control (thread_id, l4_nilthread, 
l4_nilthread,
+                                              l4_nilthread, (void *) -1);
+                 assert (err == 0);
+                 *next_thread = thread->next;
+                 thread_dealloc (thread);
+                 break;
+               }
+             else
+               {
+                 thread = thread->next;
+                 next_thread = &thread->next;
+               }
+           }
+
+         if (!thread)
+           {
+             err = EINVAL;
+             break;
+           }
+       }
+      
+      num_deallocated = i;
+      task->nr_threads -= num_deallocated;
+    }
+
+  l4_msg_clear (ctx->msg);
+  l4_msg_append_word (ctx->msg, num_deallocated);
+  l4_msg_append_word (ctx->msg, err);
+
+  return 0;
+}
+  
+error_t
 task_demuxer (hurd_cap_rpc_context_t ctx)
 {
   error_t err = 0;
 
   switch (l4_msg_label (ctx->msg))
     {
-      /* TASK_THREAD_ALLOC */
+      /* TASK_THREADS_CREATE */
     case 512:
-      err = task_thread_alloc (ctx);
+      err = task_threads_create (ctx);
+      break;
+      
+      /* TASK_THREADS_TERMINATE */
+    case 513:
+      err = task_threads_terminate (ctx);
+      break;
+      
+      /* TASK_TERMINATE */
+    case 514:
+      err = task_terminate (ctx);
       break;
 
     default:
@@ -118,7 +416,6 @@ task_demuxer (hurd_cap_rpc_context_t ctx
 
 
 
-static struct hurd_cap_class task_class;
 
 /* Initialize the task class subsystem.  */
 error_t
@@ -133,7 +430,7 @@ task_class_init ()
 /* Allocate a new task object with the task ID TASK_ID and the
    NR_THREADS threads listed in THREADS (which are already allocated
    for that task.  The object returned is locked and has one
-   reference.  */
+   reference.  Only used at bootstrap.  */
 error_t
 task_alloc (l4_word_t task_id, unsigned int nr_threads,
            l4_thread_id_t *threads, task_t *r_task)
@@ -142,12 +439,15 @@ task_alloc (l4_word_t task_id, unsigned 
   hurd_cap_obj_t obj;
   task_t task;
 
+  debug ("Allocating initial capability for task_id %d (nr_threads: %d)\n",
+        task_id, nr_threads);
   err = hurd_cap_class_alloc (&task_class, &obj);
   if (err)
     return err;
   task = hurd_cap_obj_to_user (task_t, obj);
 
   task->task_id = task_id;
+  task->nr_threads = nr_threads;
 
   /* Add the threads from back to front.  */
   task->threads = NULL;
@@ -180,7 +480,11 @@ task_alloc (l4_word_t task_id, unsigned 
       thread->next = task->threads;
       task->threads = thread;
     }
-  task->nr_threads = nr_threads;
+  
+  task->group.next = task;
+  task->group.common = malloc (sizeof (struct task_group_common));
+  assert (task->group.common);
+  pthread_mutex_init (task->group.lock, NULL);
 
   *r_task = task;
   return 0;
diff -rN -u -p old-darcs/hurd-l4/task/task.h new-darcs/hurd-l4/task/task.h
--- old-darcs/hurd-l4/task/task.h       2005-02-14 02:22:16.000000000 +0100
+++ new-darcs/hurd-l4/task/task.h       2005-04-16 23:12:20.000000000 +0200
@@ -79,6 +79,24 @@ void thread_dealloc (thread_t thread);
 
 /* Task objects.  */
 
+/* Things common to a whole task group can be put here.  */
+struct task_group_common
+{
+  /* If you have to lock simultaneously more than one task (like in
+     deletion of a task), you have to unlock all the tasks you have
+     locked, take the group lock, and then lock the tasks you want.
+     You can release it when you know you don't have anymore task to
+     lock.  Taking this lock for insertion is not necessary because
+     there is only one lock in the group to take. 
+
+     LOCK has to be the first field in the struct.  */
+  pthread_mutex_t lock;
+
+  /* FIXME: Eventually task group ID, or other stuffs.  */
+};
+typedef struct task_group_common *task_group_common_t;
+
+
 struct task
 {
   /* This is for fast removal from the task_id_to_task hash table.  */
@@ -90,13 +108,27 @@ struct task
      a local thread ID).  */
   hurd_task_id_t task_id;
 
+  /* Each task belong to a group.  Upon creation, the new task enters
+     into its parent's group.  However, the change_group RPC can
+     modify this.  */
+  struct
+  {
+    union
+    {
+      task_group_common_t common;
+      pthread_mutex_t *lock;
+    };
+
+    /* The group is a circular linked list.  */
+    struct task *next;
+  } group;
+
   /* The threads in this task.  */
   unsigned int nr_threads;
   thread_t threads;
 };
 typedef struct task *task_t;
 
-
 /* Initialize the task class subsystem.  */
 error_t task_class_init ();
 
@@ -107,6 +139,11 @@ error_t task_class_init ();
 error_t task_alloc (l4_word_t task_id, unsigned int nr_threads,
                    l4_thread_id_t *threads, task_t *r_task);
 
+/* Bits in the flag passed to task_threads_create.  */
+enum {
+  TASK_NEW_TASK = 0, /* Allocate the threads in a new created task.  */
+};
+
 
 extern pthread_mutex_t task_id_to_task_lock;
 

Thanks,
Matthieu

reply via email to

[Prev in Thread] Current Thread [Next in Thread]