1 /* Copyright (C) 2010-2013 Free Software Foundation, Inc.
2 This file is part of the UPC runtime Library.
3 Written by Gary Funck <gary@intrepid.com>
4 and Nenad Vukicevic <nenad@intrepid.com>
6 This file is part of GCC.
8 GCC is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GCC is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
33 #include "upc_lock_sup.h"
35 /* UPC lock implementation.
37 The UPC lock functions use MCS locks as described in the
38 Mellor-Crummey and Scott paper: "Algorithms for Scalable Synchronization
39 on Shared-Memory Multiprocessors", ACM Transaction on Computer Systems,
42 The following data structures are used in this implementation:
45 A data structure in the shared address space used to link threads
46 waiting on the lock. Each thread inserts itself on the list with
47 a link block that but has affinity to the thread itself. It has
48 three fields: (1) next - link list pointer, (2) signal - notification
49 of lock ownership transfer, (3) free flag - link block was freed by
50 some other thread. A link block has affinity to the owner of the block.
51 * Lock Link block reference
52 A 64 bits container for pointer to shared that contains only a thread
53 number and address field. Link reference allow the lock routines to
54 efficiently execute atomic operations on shared pointers.
55 * Lock structure (upc_lock_t)
56 A lock is a data structure living in the shared memory space.
57 Contains two lock link references: (1) last - a lock link reference
58 to the link block of the last thread on the lock waiting list,
59 (2) owner - link reference to the lock's owner link block. The lock
60 data structure has affinity of the thread that created the lock.
62 The lock data structure goes though the following states:
65 Last link block reference is NULL.
67 Last link block reference points to the last thread's link block on
68 the waiting queue. Owner link reference points to the owner thread's
69 link block. Only the owner of the lock is allowed to manipulate the
70 owner's link reference in the lock data structure.
72 The following operations are performed on the lock data structure:
75 Thread allocates a link block and write its reference to the 'last'
76 filed of the lock data structure by performing an atomic SWAP
77 operation. If returned value is NULL, thread is the owner of the
78 lock. Otherwise a link reference to the last thread on the waiting
79 queue is returned and thread needs to link itself on the waiting
82 Attempt to write a NULL link block reference into the lock's 'last'
83 field with atomic CSWAP operation. If successful, lock is released.
84 Otherwise, the ownership of the lock must be passed to the first
85 thread on the wait queue.
86 * Lock allocation/free
87 Lock is allocated from the shared memory space or from the local
88 free list. They are freed by placing the lock data structure on
89 the local lock free list if the lock has affinity of the thread that
90 releases it. Otherwise lock's memory is released.
93 struct upc_lock_link_cache_struct
95 upc_lock_link_t lock_links[GUPCR_MAX_LOCKS];
97 typedef struct upc_lock_link_cache_struct upc_lock_link_cache_t;
99 /* Array of lock links managed as a per-thread free list. */
100 static shared upc_lock_link_cache_t upc_lock_link_cache[THREADS];
102 /* Per thread lock link free list. */
103 static upc_lock_link_t *upc_lock_links;
104 /* Null link block reference. Used for CSWAP operations. */
105 upc_link_ref null_link = {.atomic = 0 };
107 /* UPC lock free list. */
108 static upc_lock_t *lock_free;
110 /* Memory allocation support. */
111 upc_lock_t *shared __upc_all_lock;
112 shared upc_lock_t __upc_alloc_lock;
114 __attribute__ ((__always_inline__))
117 upc_new_lock_init (upc_lock_t *lock)
119 lock->last.atomic = 0;
120 lock->owner_link.atomic = 0;
123 /* Lock link block utilities. */
125 /* Lock link block is a data structure that links
126 lock waiting threads. It is located in the
127 shared space of the thread waiting for the lock.
128 They are locally managed with a free list rooted
132 The current design for memory allocation uses
133 UPC locks in alloc/free routines. Thus, link blocks
134 cannot be allocate with the UPC memory allocation
137 /* Initialize lock link block free list.
138 NOTE: Link with local addresses for faster access. */
140 upc_lock_link_init (void)
142 shared [] upc_lock_link_t *slink = upc_lock_link_cache[MYTHREAD].lock_links;
143 upc_lock_link_t *link = (upc_lock_link_t *) slink;
144 upc_lock_links = link;
145 memset (link, '\0', sizeof (upc_lock_link_cache_t));
146 for (int i = 0; i < (GUPCR_MAX_LOCKS - 1); i++)
148 link[i].link_ref = upc_to_link_ref (slink++);
149 link[i].link = &link[i + 1];
151 link[GUPCR_MAX_LOCKS - 1].link_ref = upc_to_link_ref (slink++);
154 /* Release lock link block. */
155 __attribute__ ((__always_inline__))
158 upc_lock_link_free (upc_lock_link_t * link)
160 SET_NULL_LOCK_REF (link->next);
162 link->link = upc_lock_links;
163 upc_lock_links = link;
166 /* Allocate lock link block. */
167 __attribute__ ((__always_inline__))
170 upc_lock_link_alloc (void)
172 upc_lock_link_t *link = upc_lock_links;
175 /* Try to find a link block that has been freed by
176 some other thread and thus not returned to the free list. */
177 upc_lock_link_t *llink = (upc_lock_link_t *)
178 upc_lock_link_cache[MYTHREAD].lock_links;
179 for (int i = 0; i < (GUPCR_MAX_LOCKS - 1); ++i)
184 upc_lock_link_free (llink);
188 link = upc_lock_links;
190 __upc_fatal ("Cannot allocate a UPC lock link. "
191 "The number of allocated per thread lock links "
192 "exceeds the configuration defined maximum of entries.");
194 upc_lock_links = link->link;
198 /* Allocate a lock and return a pointer to it.
199 This is not a collective function. */
201 upc_global_lock_alloc (void)
207 lock_free = lock->free_link;
211 /* Allocate space for the lock from shared memory with
212 affinity to the calling thread. */
213 lock = upc_alloc (sizeof (upc_lock_t));
215 __upc_fatal ("Cannot allocate memory for the lock");
217 upc_new_lock_init (lock);
221 /* Free all lock resources.
222 If lock has affinity to the calling thread it is released on the
223 local free list. If 'lock' is a null pointer, no action occurs.
224 Otherwise, if the argument does not match a pointer earlier
225 returned by the alloc function, or if the lock has been de-allocated
226 by a previous call to 'upc_lock_free' the behavior is undefined. */
229 upc_lock_free (upc_lock_t *lock)
234 /* Release the link block if this thread owns the lock. */
235 owner = lock->owner_link;
236 if (!NULL_LOCK_REF (owner))
238 shared upc_lock_link_t *link = upc_from_link_ref (owner);
239 if (MYTHREAD == (int) upc_threadof (link))
241 upc_lock_link_free ((upc_lock_link_t *) link);
246 if (MYTHREAD == (int) upc_threadof (lock))
248 /* Release it on the local free list. */
249 lock->free_link = lock_free;
256 /* Collective free all lock resources. */
258 upc_all_lock_free (upc_lock_t *lock)
263 /* Release the link block if this thread owns the lock. */
264 owner = lock->owner_link;
265 if (!NULL_LOCK_REF (owner))
267 shared upc_lock_link_t *link = upc_from_link_ref (owner);
268 if (MYTHREAD == (int) upc_threadof (link))
270 upc_lock_link_free ((upc_lock_link_t *) link);
273 if (MYTHREAD == (int) upc_threadof (lock))
275 /* Release it on the local free list. */
276 lock->free_link = lock_free;
282 /* Allocate a lock and return a pointer to it.
283 'upc_all_lock_alloc' is a collective function. */
285 upc_all_lock_alloc (void)
294 lock_free = lock->free_link;
298 lock = upc_alloc (sizeof (upc_lock_t));
300 __upc_fatal ("Cannot allocate memory for the lock");
302 upc_new_lock_init (lock);
303 __upc_all_lock = lock;
306 return __upc_all_lock;
309 /* UPC lock acquire. */
311 upc_lock (upc_lock_t *lock)
313 upc_lock_link_t *link;
314 upc_link_ref old_link_ref;
315 link = upc_lock_link_alloc ();
317 /* Insert this thread on the waiting list. */
318 upc_link_ref_swap (&lock->last, &old_link_ref, link->link_ref);
319 if (!NULL_LOCK_REF (old_link_ref))
321 /* We have to wait. "old_link_ref" contains a reference
322 to the last thread on the wait queue. */
323 shared upc_lock_link_t *rmt_link = upc_from_link_ref (old_link_ref);
324 upc_link_ref_put ((shared upc_link_ref *) &rmt_link->next,
326 /* Wait for lock ownership notification. */
327 __upc_spin_until (link->signal);
329 lock->owner_link = link->link_ref;
333 /* UPC lock acquire attempt.
334 Return 1 if lock is acquired, 0 otherwise. */
336 upc_lock_attempt (upc_lock_t *lock)
338 upc_lock_link_t *link;
340 /* No need go further if lock is unavailable. */
341 if (!NULL_LOCK_REF (upc_link_ref_last (&lock->last)))
343 /* Try to allocate the lock. */
344 link = upc_lock_link_alloc ();
345 compare_ok = upc_link_ref_cswap (&lock->last, null_link, link->link_ref);
348 lock->owner_link = link->link_ref;
353 upc_lock_link_free (link);
358 /* UPC lock release. */
360 upc_unlock (upc_lock_t *lock)
362 upc_lock_link_t *link;
363 upc_link_ref link_ref = lock->owner_link;
367 __upc_fatal ("Trying to release a NULL lock");
368 if (NULL_LOCK_REF (link_ref))
369 __upc_fatal ("Trying to release a lock that is not locked");
371 link = (upc_lock_link_t *) upc_from_link_ref (link_ref);
373 /* Try to release the lock by trying to write a NULL into lock
374 block (last). Use CSWAP with link_ref as expected. */
375 compare_ok = upc_link_ref_cswap (&lock->last, link_ref, null_link);
378 /* Another thread is already waiting for the lock,
379 pass the ownership. */
380 /* Make sure that waiting thread completed insertion on the
382 __upc_spin_until (!NULL_LOCK_REF (upc_link_ref_get (&link->next)));
383 /* Notify the waiting thread that it now owns the lock. */
385 shared upc_lock_link_t *rmt_link;
386 rmt_link = upc_from_link_ref (link->next);
387 rmt_link->signal = 1;
390 upc_lock_link_free (link);
393 /* Heap manager lock support. */
396 __upc_acquire_alloc_lock ()
398 upc_lock (&__upc_alloc_lock);
402 __upc_release_alloc_lock ()
404 upc_unlock (&__upc_alloc_lock);
407 /* Initialize UPC lock resources. */
409 __upc_lock_init (void)
411 upc_lock_link_init ();
414 /* Heap manager lock must be manually initialized. */
416 upc_new_lock_init (&__upc_alloc_lock);