l4-hurd
[Top][All Lists]
Advanced

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

Task server implementation and integration among the other core servers


From: Matthieu Lemerre
Subject: Task server implementation and integration among the other core servers
Date: Mon, 21 Mar 2005 04:30:12 +0000
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux)

Hi,

I have written an implementation of the task server, and modified the
other servers so that they use the new interfaces. The patch is a bit
huge, so here are the keypoints:

* The task server has three main RPCs: task_threads_create,
  task_threads_terminate, task_terminate (the names are maybe not well
  chosen, I tried to mimic Mach ones). task_threads_create is
  responsible for both task and threads creation.

  I wrote some wrappers for common operations (empty task container
  creation, thread allocation ...)

* Task now decides itself on the utcb of each thread, so the utcb
  argument of task_thread_alloc is no longer necessary. This is
  because we have to store the utcb for a task, so we should take
  profit of it :). I modified wortel to provide the core servers' utcb
  to task for their allocation.

* Task groups are implemented using a circular singled-linked list of
  tasks.  Thus deletion could be quite long (we have to iterate over each
  task to find the parent), but in practice, I think that most of the
  task groups will have 1 or 2 tasks, and that structure allows the
  simplest algorithms I think.

  Insertion of a task in a group is not a problem, but deletion requires
  two locks (one for the parent, one for the task to be deleted), so to
  avoid a deadlock, I decided that if we can't acquire the two locks
  immediatly, we just return with EAGAIN (the client has to do the
  task_terminate RPC again). That should not happen very often, so I
  guess that it's not a problem.

  Deletion of a whole task group require to lock every task in a group,
  so there is a deadlock problem here. This problem is maybe reduced if
  only the manager capability (proc) can do that operation (it just have
  to make sure that it does not do it twice on the same task group).

  That's what I proposed in the attached C files (which are more or less
  functional drafts of operations on task-groups.)
 

Here's the changelog for deva:

2005-03-21  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.

for ruth:

2005-03-21  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.

For libl4:

2005-03-21  Matthieu Lemerre  <address@hidden>

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


For wortel:

2005-03-21  Matthieu Lemerre  <address@hidden>

        * wortel.c (setup_components): Set the utcb field for the wortel
        modules.
        (start_physmem): Set the utcb field for wortel module physmem.
        (start_elf): Use wortel module utcb field to setup the utcb.
        (serve_bootstrap_requests): Added WORTEL_MSG_SPACE_CONTROL RPC
        implementation. Added utcb in the reply to
        WORTEL_MSG_GET_TASK_CAP_REQUEST.
        * wortel.h (wortel_get_task_cap_request): Now retrieve the utcb
        argument.
        * wortel-intern.h (struct wortel_module): Added utcb field.


For task:

2005-03-21  Matthieu Lemerre  <address@hidden>

        * task-class.c (TASK_NEW): 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).

        * task.c (create_bootstrap_caps): Retrieve the utcb from wortel,
        and pass it to task alloc.
        * task.h (struct task): Added group_next, kip and utcb fields.
        (task_alloc): New utcb argument.


Here's the patch:

diff -rup mainline/darcs/hurd-l4/deva/deva.c hurd-l4/deva/deva.c
--- mainline/darcs/hurd-l4/deva/deva.c  2005-02-14 01:22:17.000000000 +0000
+++ hurd-l4/deva/deva.c 2005-03-20 14:19:10.000000000 +0000
@@ -154,24 +154,27 @@ 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,
+                           &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.  */
@@ -184,36 +187,9 @@ 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);
-
-  /* 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 -rup mainline/darcs/hurd-l4/deva/ia32-cmain.c hurd-l4/deva/ia32-cmain.c
--- mainline/darcs/hurd-l4/deva/ia32-cmain.c    2005-02-14 01:22:17.000000000 
+0000
+++ hurd-l4/deva/ia32-cmain.c   2005-03-20 13:45:58.000000000 +0000
@@ -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,10 +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 *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                           + l4_utcb_size ()),
+                          /* FIXME: this should be a PHYSMEM thread.  */
+                          l4_my_global_id (),
                           &pager_tid);
   if (err)
     {
diff -rup mainline/darcs/hurd-l4/deva/task-user.c hurd-l4/deva/task-user.c
--- mainline/darcs/hurd-l4/deva/task-user.c     2005-02-14 01:22:17.000000000 
+0000
+++ hurd-l4/deva/task-user.c    2005-03-20 19:29:54.000000000 +0000
@@ -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,26 @@
 #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.  If KIP_BASE or UTCB is
+   non-null, it is used for the next space_control on the address
+   space (which happens when the number of threads in a task goes from
+   0 to 1).  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,
+                    void *kip_base,
+                    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 +59,166 @@ 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, (l4_word_t) kip_base);
+  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,
+                             (void *) -1, 0, TASK_NEW, 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,
+                 void *kip_base,
+                 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,
+                             kip_base, utcb, TASK_NEW, new_task,
+                             &num_allocated_threads, thread_id);
+}
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager.  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 *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,
+                             (void *) -1, 0, 0, &new_task,
+                             num_allocated_threads, allocated_threads_ids);
+}
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager.  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_thread_id_t *thread_id)
+{
+  l4_word_t num_allocated_threads;
+  error_t err;
+  err = task_threads_alloc (task_server, task, 1, pager,
+                           &num_allocated_threads, thread_id);
+  if (err)
+    return err;
+  if (num_allocated_threads != 1)
+    return EAGAIN;
   return 0;
 }
diff -rup mainline/darcs/hurd-l4/deva/task-user.h hurd-l4/deva/task-user.h
--- mainline/darcs/hurd-l4/deva/task-user.h     2005-02-14 01:22:17.000000000 
+0000
+++ hurd-l4/deva/task-user.h    2005-03-20 19:29:43.000000000 +0000
@@ -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,91 @@
 #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.  If KIP_BASE or UTCB is
+   non-null, it is used for the next space_control on the address
+   space (which happens when the number of threads in a task goes from
+   0 to 1).  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,
+                    void *kip_base,
+                    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,
+                 void *kip_base,
+                 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.  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 *num_allocated_threads,
+                   l4_thread_id_t *allocated_threads_ids);
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager.  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_thread_id_t *thread_id);
+
+#define TASK_NEW 1
 
 #endif /* HURD_TASK_USER_H */
diff -rup mainline/darcs/hurd-l4/libl4/l4/gnu/thread.h 
hurd-l4/libl4/l4/gnu/thread.h
--- mainline/darcs/hurd-l4/libl4/l4/gnu/thread.h        2005-02-14 
01:22:16.000000000 +0000
+++ hurd-l4/libl4/l4/gnu/thread.h       2005-03-20 16:55:37.000000000 +0000
@@ -303,6 +303,12 @@ l4_set_virtual_sender (l4_thread_id_t t)
   _L4_set_virtual_sender (t);
 }
 
+static inline l4_word_t *
+_L4_attribute_always_inline
+l4_utcb (void)
+{
+  return _L4_utcb ();
+}
 
 
 static inline void
diff -rup mainline/darcs/hurd-l4/ruth/ia32-cmain.c hurd-l4/ruth/ia32-cmain.c
--- mainline/darcs/hurd-l4/ruth/ia32-cmain.c    2005-02-14 01:22:16.000000000 
+0000
+++ hurd-l4/ruth/ia32-cmain.c   2005-03-20 13:58:30.000000000 +0000
@@ -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,10 +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 *)
-                          (l4_address (__hurd_startup_data->utcb_area)
-                           + l4_utcb_size ()),
+                          /* FIXME: this should be a PHYSMEM thread.  */
+                          l4_my_global_id (),
                           &pager_tid);
   if (err)
     {
diff -rup mainline/darcs/hurd-l4/ruth/ruth.c hurd-l4/ruth/ruth.c
--- mainline/darcs/hurd-l4/ruth/ruth.c  2005-02-14 01:22:16.000000000 +0000
+++ hurd-l4/ruth/ruth.c 2005-03-20 22:22:16.451051800 +0000
@@ -81,24 +81,26 @@ 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,
+                           &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 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.  */
@@ -111,29 +113,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 -rup mainline/darcs/hurd-l4/ruth/task-user.c hurd-l4/ruth/task-user.c
--- mainline/darcs/hurd-l4/ruth/task-user.c     2005-02-14 01:22:16.000000000 
+0000
+++ hurd-l4/ruth/task-user.c    2005-03-20 19:29:54.000000000 +0000
@@ -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,26 @@
 #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.  If KIP_BASE or UTCB is
+   non-null, it is used for the next space_control on the address
+   space (which happens when the number of threads in a task goes from
+   0 to 1).  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,
+                    void *kip_base,
+                    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 +59,166 @@ 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, (l4_word_t) kip_base);
+  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,
+                             (void *) -1, 0, TASK_NEW, 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,
+                 void *kip_base,
+                 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,
+                             kip_base, utcb, TASK_NEW, new_task,
+                             &num_allocated_threads, thread_id);
+}
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager.  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 *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,
+                             (void *) -1, 0, 0, &new_task,
+                             num_allocated_threads, allocated_threads_ids);
+}
+
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager.  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_thread_id_t *thread_id)
+{
+  l4_word_t num_allocated_threads;
+  error_t err;
+  err = task_threads_alloc (task_server, task, 1, pager,
+                           &num_allocated_threads, thread_id);
+  if (err)
+    return err;
+  if (num_allocated_threads != 1)
+    return EAGAIN;
   return 0;
 }
diff -rup mainline/darcs/hurd-l4/ruth/task-user.h hurd-l4/ruth/task-user.h
--- mainline/darcs/hurd-l4/ruth/task-user.h     2005-02-14 01:22:16.000000000 
+0000
+++ hurd-l4/ruth/task-user.h    2005-03-20 19:29:43.000000000 +0000
@@ -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,91 @@
 #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.  If KIP_BASE or UTCB is
+   non-null, it is used for the next space_control on the address
+   space (which happens when the number of threads in a task goes from
+   0 to 1).  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,
+                    void *kip_base,
+                    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,
+                 void *kip_base,
+                 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.  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 *num_allocated_threads,
+                   l4_thread_id_t *allocated_threads_ids);
+
+/* Allocate NUM threads for the task TASK, using pager PAGER as the
+   pager.  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_thread_id_t *thread_id);
+
+#define TASK_NEW 1
 
 #endif /* HURD_TASK_USER_H */
diff -rup mainline/darcs/hurd-l4/task/task-class.c hurd-l4/task/task-class.c
--- mainline/darcs/hurd-l4/task/task-class.c    2005-02-14 01:22:16.000000000 
+0000
+++ hurd-l4/task/task-class.c   2005-03-20 19:30:29.000000000 +0000
@@ -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,72 +31,328 @@
 
 #include "task.h"
 
+#define TASK_NEW(flag) \
+    (flag & 1)
+
+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;
 
   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.  */
 }
 
 
+
 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);
+
+  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;
+      error_t lock_error;
+      
+    task_terminate_again:
+      
+      do 
+       {
+         previous = previous->group_next;
+       } while (previous->group_next != task);
+  
+      previous_obj = hurd_cap_obj_from_user (task_t, previous);
+
+      /* This avoids a possible deadlock.  */
+      lock_error = pthread_mutex_trylock (&previous_obj->lock);
+      if (lock_error)
+       /* Try later.  Hopefully this should unlikely happen.  */
+       return EAGAIN;
+      
+      if (previous->group_next == task)
+       previous->group_next = task->group_next;
+      else
+       {
+         pthread_mutex_unlock (&previous_obj->lock);
+         goto task_terminate_again;
+       }
+      pthread_mutex_unlock (&previous_obj->lock);
+    }
+
+  /* FIXME : Send task death notifications to each task which
+     holds a task info capability.  */
+  hurd_cap_obj_drop (ctx->obj);
+          
+  /* FIXME: Return the task id to the list of free task ids.  */
+  return 0;
+}
+
+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 kip_base = l4_msg_word (ctx->msg, 3);
+  l4_fpage_t utcb = l4_msg_word (ctx->msg, 4);
+  l4_word_t flag = l4_msg_word (ctx->msg, 5);
+
   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);
+  if (num_requested_threads >= 60)
+    num_requested_threads = 60;
+      
+  if (TASK_NEW (flag))
+    {
+      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;
+       }
 
-  err = thread_alloc (&thread);
-  if (err)
-    return err;
+      debug ("Allocating new task with task id %d\n", task_id);
+      
+      task->nr_threads = 0;
+      task->group_next = parent_task->group_next;
+    }
+  else
+    {
+      task = parent_task;
+      handle = l4_msg_word (ctx->msg, 0);
+    }
 
-  thread->thread_id = l4_global_id (l4_thread_no (thread->thread_id),
-                                   task->task_id);
 
-  /* 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 (utcb)
+    task->utcb = utcb;
+    
+  if (kip_base != -1)
+    task->kip = l4_fpage_log2 (kip_base, l4_kip_area_size_log2 ());
+  
+  l4_word_t utcb_location = l4_address (task->utcb);
+
+  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;
+  }
+
+  l4_word_t allocated_threads = 0;
+
+  if (task->nr_threads)
+    utcb_location += task->nr_threads * l4_utcb_size ();
+  else if (num_requested_threads)
     {
-      /* FIXME: Convert error codes in wortel.h.  */
-      thread_dealloc (thread);
-      return EINVAL;
+      thread_t thread;
+      task_alloc_thread (&thread, l4_nilthread);
+      if (err)
+       goto no_thread;
+
+      l4_word_t control;
+      err = wortel_space_control (thread->thread_id, 0,
+                                 task->kip,
+                                 task->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);
+      
+      num_requested_threads --;
+      allocated_threads = 1;
     }
+  
+  /* Allocates new threads for that task.  */
 
-  thread->next = task->threads;
-  task->threads = thread;
-  task->nr_threads++;
+  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 (TASK_NEW (flag))
+    {
+      hurd_cap_obj_unlock (obj);
+      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;
+       }
+      parent_task->group_next = task;
+    }
+     
+  /* 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;
+    }
 
   return 0;
 }
 
 
+/* Signature: task_deallocate_threads (handle,
+   nr_threads (0 == tous)),
+   threads_ids */
+
+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)
+    {
+      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)
 {
@@ -104,9 +360,19 @@ task_demuxer (hurd_cap_rpc_context_t ctx
 
   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 +397,6 @@ task_demuxer (hurd_cap_rpc_context_t ctx
 
 
 
-static struct hurd_cap_class task_class;
 
 /* Initialize the task class subsystem.  */
 error_t
@@ -133,21 +411,26 @@ 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)
+           l4_fpage_t utcb, l4_thread_id_t *threads,
+           task_t *r_task)
 {
   error_t err;
   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;
+  task->utcb = utcb;
 
   /* Add the threads from back to front.  */
   task->threads = NULL;
@@ -180,7 +463,8 @@ task_alloc (l4_word_t task_id, unsigned 
       thread->next = task->threads;
       task->threads = thread;
     }
-  task->nr_threads = nr_threads;
+  
+  task->group_next = task;
 
   *r_task = task;
   return 0;
diff -rup mainline/darcs/hurd-l4/task/task.c hurd-l4/task/task.c
--- mainline/darcs/hurd-l4/task/task.c  2005-02-14 01:22:16.000000000 +0000
+++ hurd-l4/task/task.c 2005-03-20 17:37:05.000000000 +0000
@@ -80,8 +80,9 @@ create_bootstrap_caps (hurd_cap_bucket_t
       hurd_task_id_t task_id;
       unsigned int nr_threads;
       l4_thread_id_t threads[L4_NUM_MRS];
-
-      task_id = wortel_get_task_cap_request (&nr_threads, threads);
+      l4_fpage_t utcb;
+       
+      task_id = wortel_get_task_cap_request (&utcb, &nr_threads, threads);
 
       if (nr_threads == 0)
        {
@@ -98,7 +99,7 @@ create_bootstrap_caps (hurd_cap_bucket_t
        {
          debug ("Creating task cap for 0x%x:", task_id);
 
-         err = task_alloc (task_id, nr_threads, threads, &task);
+         err = task_alloc (task_id, nr_threads, utcb, threads, &task);
          if (err)
            panic ("task_alloc: %i", err);
 
diff -rup mainline/darcs/hurd-l4/task/task.h hurd-l4/task/task.h
--- mainline/darcs/hurd-l4/task/task.h  2005-02-14 01:22:16.000000000 +0000
+++ hurd-l4/task/task.h 2005-03-20 18:00:44.000000000 +0000
@@ -90,9 +90,19 @@ 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.  The group is a circular linked list.  */
+  struct task *group_next;
+
   /* The threads in this task.  */
   unsigned int nr_threads;
   thread_t threads;
+
+  /* Kernel-mapped memory objects.  */
+  l4_fpage_t kip;
+  l4_fpage_t utcb;
+  
 };
 typedef struct task *task_t;
 
@@ -105,7 +115,8 @@ error_t task_class_init ();
    for that task.  The object returned is locked and has one
    reference.  */
 error_t task_alloc (l4_word_t task_id, unsigned int nr_threads,
-                   l4_thread_id_t *threads, task_t *r_task);
+                   l4_fpage_t utcb, l4_thread_id_t *threads,
+                   task_t *r_task);
 
 
 extern pthread_mutex_t task_id_to_task_lock;
diff -rup mainline/darcs/hurd-l4/wortel/wortel-intern.h 
hurd-l4/wortel/wortel-intern.h
--- mainline/darcs/hurd-l4/wortel/wortel-intern.h       2005-02-14 
01:22:16.000000000 +0000
+++ hurd-l4/wortel/wortel-intern.h      2005-03-19 00:23:47.000000000 +0000
@@ -116,9 +116,12 @@ struct wortel_module
      while they have the same version ID (which is equal to task_id).
      Initialized just before the task is started.  */
   unsigned int nr_extra_threads;
-};
 
+  /* The initial UTCB location will be used by task for further thread
+     allocation. */
+  l4_fpage_t utcb;
 
+};
 enum wortel_module_type
   {
     MOD_PHYSMEM = 0,
diff -rup mainline/darcs/hurd-l4/wortel/wortel.c hurd-l4/wortel/wortel.c
--- mainline/darcs/hurd-l4/wortel/wortel.c      2005-02-14 01:22:16.000000000 
+0000
+++ hurd-l4/wortel/wortel.c     2005-03-20 16:56:28.000000000 +0000
@@ -251,6 +251,12 @@ setup_components (void)
             provided by this task.  */
          mods[i].server_thread = l4_global_id (thread_no++, task_id);
 
+         /* The utcb location for each task is known in advance.  */
+         mods[i].utcb = l4_fpage_log2 (((l4_word_t) HURD_STARTUP_ADDR)
+                                       + HURD_STARTUP_SIZE
+                                       + l4_kip_area_size (),
+                                       l4_utcb_area_size_log2 ());
+
          /* physmem needs three extra threads (one main thread that
             is not the server thread and two alternating worker
             threads), because it is started before the task server is
@@ -335,11 +341,12 @@ start_physmem (void)
      accidently exceed the wortel end address.  */
   /* FIXME: In any case, we have to pass the UTCB area size (and
      location) to physmem.  */
+  mods[MOD_PHYSMEM].utcb = l4_fpage_log2 (wortel_start + l4_kip_area_size (),
+                                         l4_utcb_area_size_log2 ());
   ret = l4_space_control (physmem, 0,
                          l4_fpage_log2 (wortel_start,
                                         l4_kip_area_size_log2 ()),
-                         l4_fpage_log2 (wortel_start + l4_kip_area_size (),
-                                        l4_utcb_area_size_log2 ()),
+                         mods[MOD_PHYSMEM].utcb,
                          l4_anythread, &control);
   if (!ret)
     panic ("could not create physmem address space: %s",
@@ -735,10 +742,7 @@ start_elf (unsigned int mod)
   phys_startup->version_major = HURD_STARTUP_VERSION_MAJOR;
   phys_startup->version_minor = HURD_STARTUP_VERSION_MINOR;
   phys_startup->flags = 0;
-  phys_startup->utcb_area = l4_fpage_log2 (((l4_word_t) HURD_STARTUP_ADDR)
-                                          + HURD_STARTUP_SIZE
-                                          + l4_kip_area_size (),
-                                          l4_utcb_area_size_log2 ());
+  phys_startup->utcb_area = mods[mod].utcb;
   phys_startup->argz = argz_len ? STARTUP_TO_VIRT (argz) : NULL;
   phys_startup->argz_len = argz_len;
   phys_startup->envz = NULL;
@@ -1231,6 +1235,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)
@@ -1242,6 +1267,8 @@ serve_bootstrap_requests (void)
              l4_msg_clear (msg);
              l4_set_msg_label (msg, 0);
              l4_msg_append_word (msg, wortel_task_id);
+             l4_msg_append_word (msg, l4_fpage_log2 ((l4_word_t) l4_utcb (),
+                                                     l4_utcb_area_size_log2 
()));
              l4_msg_append_word (msg, l4_my_global_id ());
              for (i = 1; i < WORTEL_THREADS; i++)
                l4_msg_append_word
@@ -1257,6 +1284,7 @@ serve_bootstrap_requests (void)
              l4_msg_clear (msg);
              l4_msg_append_word
                (msg, l4_version (mods[MOD_ROOT_FS].server_thread));
+             l4_msg_append_word (msg, mods[MOD_ROOT_FS].utcb);
              l4_msg_load (msg);
              l4_reply (from);
            }
@@ -1276,6 +1304,7 @@ serve_bootstrap_requests (void)
              l4_msg_clear (msg);
              l4_set_msg_label (msg, 0);
              l4_msg_append_word (msg, mods[cur_task].task_id);
+             l4_msg_append_word (msg, mods[cur_task].utcb);
              l4_msg_append_word (msg, mods[cur_task].server_thread);
              for (i = 1; i <= mods[cur_task].nr_extra_threads; i++)
                l4_msg_append_word
diff -rup mainline/darcs/hurd-l4/wortel/wortel.h hurd-l4/wortel/wortel.h
--- mainline/darcs/hurd-l4/wortel/wortel.h      2005-02-14 01:22:16.000000000 
+0000
+++ hurd-l4/wortel/wortel.h     2005-03-20 19:02:57.000000000 +0000
@@ -376,7 +378,8 @@ wortel_get_first_free_thread_no (void)
 /* Get the next task capability request.  */
 static inline hurd_task_id_t
 __attribute__((always_inline))
-wortel_get_task_cap_request (unsigned int *nr_threads, l4_thread_id_t *threads)
+  wortel_get_task_cap_request (l4_fpage_t *utcb, unsigned int *nr_threads,
+                              l4_thread_id_t *threads)
 {
   l4_msg_tag_t tag;
   hurd_task_id_t task_id;
@@ -393,7 +396,8 @@ wortel_get_task_cap_request (unsigned in
   *nr_threads = 0;
   mr = 1;
   l4_store_mr (mr++, &task_id);
-  nr_items = l4_untyped_words (tag) - 1;
+  l4_store_mr (mr++, utcb);
+  nr_items = l4_untyped_words (tag) - 2;
   while (nr_items--)
     l4_store_mr (mr++, &threads[(*nr_threads)++]);
 

For archive, because we will need that when working on proc, RPCs on manager 
capabilities:

/* task-manager-class.c - Task manager class for the task server.
   Copyright (C) 2005 Free Software Foundation, Inc.
   Written by Matthieu Lemerre.

   This file is part of the GNU Hurd.

   The GNU Hurd is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU Hurd is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>

#include <l4.h>
#include <hurd/cap-server.h>
#include <hurd/wortel.h>

#include "task.h"


static struct hurd_cap_class task_manager_class;



/* Should instead be a manager capability RPC.  */
/* FIXME: il faut peut etre aussi mettre des locks quand on lit: que
   se passe-t-il si le node qu'on lit disparait? ou est modifie? Du
   coup c'est plus complique.  Le mieux serait d'avoir un lock qui
   serait partage par tous les membres du groupe, comme ca plus de pb.
   Par contre faut allouer ce lock (avec un slab?), mais ca suffit
   pas: il faut aussi locker les elements qu'on modifie.  */
error_t
task_list_group (hurd_cap_rpc_context_t ctx)
{
  error_t err = 0;
  l4_word_t task_handle = l4_msg_word (ctx->msg, 1);
  struct hurd_cap_ctx_cap_use *cap_ctx = alloca (hurd_cap_ctx_size ());
  hurd_cap_obj_t obj;
  
  hurd_task_id_t task_id[60];
  l4_word_t task_nb = 0;

  hurd_cap_obj_unlock (ctx->obj);
  err =  hurd_cap_ctx_start_cap_use (ctx, task_handle, &task_class,
                                     cap_ctx, &obj);
  if (err)
    return err;
  
  task_t task = hurd_cap_obj_to_user (task_t, obj);
  hurd_cap_obj_unlock (obj);
  
  do 
    {
      task_id[task_nb] = task->task_id;
      task = task->group_next;
      task_nb++;
    } while (task != hurd_cap_obj_to_user (task_t, obj)
             && task_nb < 60);
  
  hurd_cap_obj_lock (obj);
  hurd_cap_ctx_end_cap_use (ctx, cap_ctx);
  hurd_cap_obj_lock (ctx->obj);

  /* Prepare reply message.  */
  l4_msg_clear (ctx->msg);
  l4_msg_append_word (ctx->msg, task_nb);
  
  int i;
  for (i = 0; i < task_nb; i++)
    l4_msg_append_word (ctx->msg, task_id[i]);

  return 0;
}

/* Should instead be a manager capability RPC.  */
error_t
task_delete_group (hurd_cap_rpc_context_t ctx)
{
  error_t err = 0;
  l4_word_t task_handle = l4_msg_word (ctx->msg, 1);

  struct hurd_cap_ctx_cap_use *cap_ctx = alloca (hurd_cap_ctx_size ());
  hurd_cap_obj_t obj;
  
  hurd_cap_obj_unlock (ctx->obj);
  err =  hurd_cap_ctx_start_cap_use (ctx, task_handle, &task_class,
                                     cap_ctx, &obj);

  /* Lock every object in the group.  */
  task_t task = hurd_cap_obj_to_user (task_t, obj);

  hurd_cap_ctx_end_cap_use (ctx, cap_ctx);


  while (task->group_next != hurd_cap_obj_to_user (task_t, obj))
    {
      task = task->group_next;
      hurd_cap_obj_lock (hurd_cap_obj_from_user (task_t, task));
    }
  
  /* Now drop each task.  */
  do 
    {
      hurd_cap_obj_drop (hurd_cap_obj_from_user (task_t, task));
    } while (task != hurd_cap_obj_to_user (task_t, obj));

  hurd_cap_obj_lock (ctx->obj);

  l4_msg_clear (ctx->msg);

  return 0;
}
  
error_t
task_manager_demuxer (hurd_cap_rpc_context_t ctx)
{
  error_t err = 0;

  switch (l4_msg_label (ctx->msg))
    {
      /* TASK_LIST_GROUP */
    case 519:
      err = task_list_group (ctx);
      break;

      /* TASK_DELETE_GROUP */
    case 520:
      err = task_delete_group (ctx);
      break;

    default:
      err = EOPNOTSUPP;
    }

  return err;
}




/* Initialize the task class subsystem.  */
error_t
task_manager_class_init ()
{
  return hurd_cap_class_init (&task_manager_class, task_t,
                              NULL, NULL, NULL, NULL,
                              task_demuxer);
}



And associated user stubs:

/* User stubs. */


/* Returns all the task ids of tasks in TASK's group.  */
error_t
task_list_group (l4_thread_id_t task_server,
                 hurd_cap_handle_t a_task,
                 hurd_cap_handle_t task,
                 l4_word_t *task_nb,
                 l4_word_t *task_ids)
{
  l4_msg_t msg;
  l4_msg_tag_t tag;

  l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);

  l4_msg_clear (msg);
  l4_set_msg_label (msg, 519);
  l4_msg_append_word (msg, a_task);
  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);

  *task_nb = l4_msg_word (msg, 0);

  int i;
  for (i = 0; i < *task_nb; i++)
    task_ids[i] = l4_msg_word (msg, i+1);

  return 0;
}

/* Delete all tasks in TASK's group.  */
error_t
task_delete_group (l4_thread_id_t task_server,
                   hurd_cap_handle_t a_task,
                   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, 520);
  l4_msg_append_word (msg, a_task);
  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;
}

Thanks,

Matthieu

reply via email to

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