summaryrefslogtreecommitdiff
path: root/libhurd-cap-server/obj-inhibit.c
blob: 4d11e9df8fdb129e60bf7a0745919948a8abccad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* obj-inhibit.c - Inhibit RPCs on a capability object.
   Copyright (C) 2004 Free Software Foundation, Inc.
   Written by Marcus Brinkmann <marcus@gnu.org>

   This file is part of the GNU Hurd.

   This program 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.

   This program 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 this program; 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 <errno.h>
#include <pthread.h>
#include <stdlib.h>

#include "cap-server-intern.h"


/* Inhibit all RPCs on the capability object CAP_OBJ (which must not
   be locked).  You _must_ follow up with a hurd_cap_obj_resume
   operation, and hold at least one reference to the object
   continuously until you did so.  */
error_t
hurd_cap_obj_inhibit (hurd_cap_obj_t obj)
{
  hurd_cap_class_t cap_class = obj->cap_class;
  error_t err;

  /* First take the class-wide lock for conditions on capability
     object states.  */
  pthread_mutex_lock (&cap_class->obj_cond_lock);

  /* Then lock the object to check its state.  */
  pthread_mutex_lock (&obj->lock);

  /* First wait until any other inhibitor has resumed the capability
     object.  This ensures that capability object inhibitions are
     fully serialized (per capability object).  */
  while (obj->state != _HURD_CAP_STATE_GREEN)
    {
      pthread_mutex_unlock (&obj->lock);
      err = hurd_cond_wait (&cap_class->obj_cond,
			    &cap_class->obj_cond_lock);
      if (err)
	{
	  /* We have been canceled.  */
	  pthread_mutex_unlock (&cap_class->obj_cond_lock);
	  return err;
	}
      pthread_mutex_lock (&obj->lock);
    }

  /* Now it is our turn to inhibit the capability object.  */
  obj->cond_waiter = pthread_self ();

  if (_hurd_cap_obj_cond_busy (obj))
    {
      _hurd_cap_list_item_t pending_rpc = obj->pending_rpcs;

      /* There are still pending RPCs (beside us).  Cancel them.  */
      while (pending_rpc)
	{
	  if (pending_rpc->thread != obj->cond_waiter)
	    pthread_cancel (pending_rpc->thread);
	  pending_rpc = pending_rpc->next;
	}

      /* Indicate that we would like to know when they have gone.  */
      obj->state = _HURD_CAP_STATE_YELLOW;

      /* The last one will shut the door.  */
      do
	{
	  pthread_mutex_unlock (&obj->lock);
	  err = hurd_cond_wait (&cap_class->obj_cond,
				&cap_class->obj_cond_lock);
	  if (err)
	    {
	      /* We have been canceled ourselves.  Give up.  */
	      obj->state = _HURD_CAP_STATE_GREEN;
	      pthread_mutex_unlock (&cap_class->obj_cond_lock);
	      return err;
	    }
	  pthread_mutex_lock (&obj->lock);
	}
      while (obj->state != _HURD_CAP_STATE_RED);
    }
  else
    obj->state = _HURD_CAP_STATE_RED;

  /* Now all pending RPCs have been canceled and are completed (except
     us), and all incoming RPCs are inhibited.  */
  pthread_mutex_unlock (&obj->lock);
  pthread_mutex_unlock (&cap_class->obj_cond_lock);

  return 0;
}


/* Resume RPCs on the capability object OBJ and wake-up all
   waiters.  */
void
hurd_cap_obj_resume (hurd_cap_obj_t obj)
{
  hurd_cap_class_t cap_class = obj->cap_class;

  pthread_mutex_lock (&cap_class->obj_cond_lock);
  pthread_mutex_lock (&cap_class->lock);

  obj->state = _HURD_CAP_STATE_GREEN;

  /* Broadcast the change to all potential waiters.  */
  pthread_cond_broadcast (&cap_class->obj_cond);

  pthread_mutex_unlock (&cap_class->lock);
  pthread_mutex_unlock (&cap_class->obj_cond_lock);
}