1/*
2   The implementation of synchronization primitives for Objective-C.
3   Copyright (C) 2008 Free Software Foundation, Inc.
4
5   Gregory Casamento <greg.casamento@gmail.com>
6
7   This file is part of the GNUstep Base Library.
8
9   This library is free software; you can redistribute it and/or
10   modify it under the terms of the GNU Lesser General Public
11   License as published by the Free Software Foundation; either
12   version 2 of the License, or (at your option) any later version.
13
14   This library is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   Library General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public
20   License along with this library; if not, write to the Free
21   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22   Boston, MA 02111 USA.
23*/
24
25#include <stdlib.h>
26#include "objc/objc.h"
27#include "objc/objc-api.h"
28#include "objc/thr.h"
29
30/*
31 * Node structure...
32 */
33typedef struct lock_node {
34  id obj;
35  objc_mutex_t lock;
36  struct lock_node *next;
37  struct lock_node *prev;
38} lock_node_t;
39
40/*
41 * Return types for the locks...
42 */
43typedef enum {
44  OBJC_SYNC_SUCCESS = 0,
45  OBJC_SYNC_NOT_OWNING_THREAD_ERROR = -1,
46  OBJC_SYNC_TIMED_OUT = -2,
47  OBJC_SYNC_NOT_INITIALIZED = -3
48} sync_return_t;
49
50static lock_node_t *lock_list = NULL;
51static objc_mutex_t table_lock = NULL;
52
53/**
54 * Initialize the table lock.
55 */
56static void
57sync_lock_init()
58{
59  if (table_lock == NULL)
60    {
61      table_lock = objc_mutex_allocate();
62    }
63}
64
65/**
66 * Find the node in the list.
67 */
68static lock_node_t*
69sync_find_node(id obj)
70{
71  lock_node_t *current = lock_list;
72
73  if (lock_list != NULL)
74    {
75      // iterate over the list looking for the end...
76      while (current != NULL)
77	{
78	  // if the current object is the one, breal and
79	  // return that node.
80	  if (current->obj == obj)
81	    {
82	      break;
83	    }
84
85	  // get the next one...
86	  current = current->next;
87	}
88    }
89  return current;
90}
91
92/**
93 * Add a node for the object, if one doesn't already exist.
94 */
95static lock_node_t*
96sync_add_node(id obj)
97{
98  lock_node_t *current = NULL;
99
100  // get the lock...
101  sync_lock_init();
102
103  // if the list hasn't been initialized, initialize it.
104  if (lock_list == NULL)
105    {
106      // instantiate the new node and set the list...
107      lock_list = malloc(sizeof(lock_node_t));
108
109      // set the current node to the last in the list...
110      current = lock_list;
111
112      // set next and prev...
113      current->prev = NULL;
114      current->next = NULL;
115    }
116  else
117    {
118      lock_node_t *new_node = NULL;
119      current = lock_list;
120
121      // look for the end of the list.
122      while (current->next)
123	{
124	  current = current->next;
125	}
126
127      // instantiate the new node...
128      new_node = malloc(sizeof(lock_node_t));
129
130      if (new_node != NULL)
131	{
132	  // set next and prev...
133	  current->next = new_node;
134	  new_node->prev = current;
135	  new_node->next = NULL;
136
137	  // set the current node to the last in the list...
138	  current = new_node;
139	}
140    }
141
142  if (current != NULL)
143    {
144      // add the object and it's lock
145      current->obj = obj;
146      current->lock = objc_mutex_allocate();
147    }
148
149  return current;
150}
151
152/**
153 * Add a lock for the object.
154 */
155int
156objc_sync_enter(id obj)
157{
158  lock_node_t *node = NULL;
159  int status = 0;
160
161  // lock access to the table until we're done....
162  sync_lock_init();
163  objc_mutex_lock(table_lock);
164
165  node = sync_find_node(obj);
166  if (node == NULL)
167    {
168      node = sync_add_node(obj);
169      if (node == NULL)
170	{
171	  // unlock the table....
172	  objc_mutex_unlock(table_lock);
173	  return OBJC_SYNC_NOT_INITIALIZED;
174	}
175    }
176
177  // unlock the table....
178  objc_mutex_unlock(table_lock);
179
180  status = objc_mutex_lock(node->lock);
181
182  // if the status is more than one, then another thread
183  // has this section locked, so we abort.  A status of -1
184  // indicates that an error occurred.
185  if (status > 1 || status == -1)
186    {
187      return OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
188    }
189
190  return OBJC_SYNC_SUCCESS;
191}
192
193/**
194 * Remove a lock for the object.
195 */
196int
197objc_sync_exit(id obj)
198{
199  lock_node_t *node = NULL;
200  int status = 0;
201
202  // lock access to the table until we're done....
203  sync_lock_init();
204  objc_mutex_lock(table_lock);
205
206  node = sync_find_node(obj);
207  if (node == NULL)
208    {
209      // unlock the table....
210      objc_mutex_unlock(table_lock);
211      return OBJC_SYNC_NOT_INITIALIZED;
212    }
213
214  status = objc_mutex_unlock(node->lock);
215
216  // unlock the table....
217  objc_mutex_unlock(table_lock);
218
219  // if the status is not zero, then we are not the sole
220  // owner of this node.  Also if -1 is returned, this indicates and error
221  // condition.
222  if (status > 0 || status == -1)
223    {
224      return OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
225    }
226
227  return OBJC_SYNC_SUCCESS;
228}
229
230