Synopsis - Cross-Reference

File: /src/Synopsis/gc/atomic_ops.c
  1/*
  2 * Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
  3 * 
  4 * Permission is hereby granted, free of charge, to any person obtaining a copy
  5 * of this software and associated documentation files (the "Software"), to deal
  6 * in the Software without restriction, including without limitation the rights
  7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8 * copies of the Software, and to permit persons to whom the Software is
  9 * furnished to do so, subject to the following conditions:
 10 * 
 11 * The above copyright notice and this permission notice shall be included in
 12 * all copies or substantial portions of the Software.
 13 * 
 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 20 * SOFTWARE. 
 21 */
 22
 23/*
 24 * Initialized data and out-of-line functions to support atomic_ops.h
 25 * go here.  Currently this is needed only for pthread-based atomics
 26 * emulation, or for compare-and-swap emulation.
 27 * Pthreads emulation isn't useful on a native Windows platform, and
 28 * cas emulation is not needed.  Thus we skip this on Windows.
 29 */
 30
 31#if defined(HAVE_CONFIG_H)
 32# include "config.h"
 33#endif
 34
 35#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__)
 36
 37#undef AO_REQUIRE_CAS
 38
 39#include <pthread.h>
 40#include <signal.h>
 41#ifdef _HPUX_SOURCE
 42# include <sys/time.h>
 43#else
 44# include <sys/select.h>
 45#endif
 46#include "atomic_ops.h"  /* Without cas emulation! */
 47
 48#ifndef AO_HAVE_double_t
 49# include "atomic_ops/sysdeps/standard_ao_double_t.h"
 50#endif
 51
 52/*
 53 * Lock for pthreads-based implementation.
 54 */
 55
 56pthread_mutex_t AO_pt_lock = PTHREAD_MUTEX_INITIALIZER;
 57
 58/*
 59 * Out of line compare-and-swap emulation based on test and set.
 60 * 
 61 * We use a small table of locks for different compare_and_swap locations.
 62 * Before we update perform a compare-and-swap, we grap the corresponding
 63 * lock.  Different locations may hash to the same lock, but since we
 64 * never acquire more than one lock at a time, this can't deadlock.
 65 * We explicitly disable signals while we perform this operation.
 66 *
 67 * FIXME: We should probably also suppport emulation based on Lamport
 68 * locks, since we may not have test_and_set either.
 69 */
 70#define AO_HASH_SIZE 16
 71
 72#define AO_HASH(x) (((unsigned long)(x) >> 12) & (AO_HASH_SIZE-1))
 73
 74AO_TS_t AO_locks[AO_HASH_SIZE] = {
 75	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 76	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 77	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 78	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 79	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 80	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 81	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 82	AO_TS_INITIALIZER, AO_TS_INITIALIZER,
 83};
 84
 85static AO_T dummy = 1;
 86
 87/* Spin for 2**n units. */
 88void AO_spin(int n)
 89{
 90  int i;
 91  AO_T j = AO_load(&dummy);
 92
 93  for (i = 0; i < (2 << n); ++i)
 94    {
 95       j *= 5;
 96       j -= 4;
 97    }
 98  AO_store(&dummy, j);
 99}
100
101void AO_pause(int n)
102{
103    if (n < 12)
104      AO_spin(n);
105    else
106      {
107        struct timeval tv;
108
109	/* Short async-signal-safe sleep. */
110	tv.tv_sec = 0;
111	tv.tv_usec = (n > 28? 100000 : (1 << (n - 12)));
112	select(0, 0, 0, 0, &tv);
113      }
114}
115
116static void lock_ool(volatile AO_TS_t *l)
117{
118  int i = 0;
119
120  while (AO_test_and_set_acquire(l) == AO_TS_SET)
121    AO_pause(++i);
122}
123
124AO_INLINE void lock(volatile AO_TS_t *l)
125{
126  if (AO_test_and_set_acquire(l) == AO_TS_SET)
127    lock_ool(l);
128}
129
130AO_INLINE void unlock(volatile AO_TS_t *l)
131{
132  AO_CLEAR(l);
133}
134
135static sigset_t all_sigs;
136
137static volatile AO_t initialized = 0;
138
139static volatile AO_TS_t init_lock = AO_TS_INITIALIZER;
140
141int AO_compare_and_swap_emulation(volatile AO_t *addr, AO_t old,
142				  AO_t new_val)
143{
144  AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
145  sigset_t old_sigs;
146  int result;
147
148  if (!AO_load_acquire(&initialized))
149    {
150      lock(&init_lock);
151      if (!initialized) sigfillset(&all_sigs);
152      unlock(&init_lock);
153      AO_store_release(&initialized, 1);
154    }
155  sigprocmask(SIG_BLOCK, &all_sigs, &old_sigs);
156  	/* Neither sigprocmask nor pthread_sigmask is 100%	*/
157  	/* guaranteed to work here.  Sigprocmask is not 	*/
158  	/* guaranteed be thread safe, and pthread_sigmask	*/
159  	/* is not async-signal-safe.  Under linuxthreads,	*/
160  	/* sigprocmask may block some pthreads-internal		*/
161  	/* signals.  So long as we do that for short periods,	*/
162  	/* we should be OK.					*/
163  lock(my_lock);
164  if (*addr == old)
165    {
166      *addr = new_val;
167      result = 1;
168    }
169  else
170    result = 0;
171  unlock(my_lock);
172  sigprocmask(SIG_SETMASK, &old_sigs, NULL);
173  return result;
174}
175
176int AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr,
177						AO_t old_val1, AO_t old_val2,
178				                AO_t new_val1, AO_t new_val2)
179{
180  AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
181  sigset_t old_sigs;
182  int result;
183
184  if (!AO_load_acquire(&initialized))
185    {
186      lock(&init_lock);
187      if (!initialized) sigfillset(&all_sigs);
188      unlock(&init_lock);
189      AO_store_release(&initialized, 1);
190    }
191  sigprocmask(SIG_BLOCK, &all_sigs, &old_sigs);
192  	/* Neither sigprocmask nor pthread_sigmask is 100%	*/
193  	/* guaranteed to work here.  Sigprocmask is not 	*/
194  	/* guaranteed be thread safe, and pthread_sigmask	*/
195  	/* is not async-signal-safe.  Under linuxthreads,	*/
196  	/* sigprocmask may block some pthreads-internal		*/
197  	/* signals.  So long as we do that for short periods,	*/
198  	/* we should be OK.					*/
199  lock(my_lock);
200  if (addr -> AO_val1 == old_val1 && addr -> AO_val2 == old_val2)
201    {
202      addr -> AO_val1 = new_val1;
203      addr -> AO_val2 = new_val2;
204      result = 1;
205    }
206  else
207    result = 0;
208  unlock(my_lock);
209  sigprocmask(SIG_SETMASK, &old_sigs, NULL);
210  return result;
211}
212
213void AO_store_full_emulation(volatile AO_t *addr, AO_t val)
214{
215  AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
216  lock(my_lock);
217  *addr = val;
218  unlock(my_lock);
219}
220
221#else /* Non-posix platform */
222
223int AO_non_posix_implementation_is_entirely_in_headers;
224
225#endif