Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / mcas / stm_lock.c
blob6c94270c2a29ba85f31786c367e5ad32347cc41d
1 /******************************************************************************
2 * stm_lock.c
4 * Lock-based software transactional memory (STM).
5 * Uses two-phase locking with multi-reader locks.
7 * Copyright (c) 2002-2003, K A Fraser
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are
11 met:
13 * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution. Neither the name of the Keir Fraser
19 * nor the names of its contributors may be used to endorse or
20 * promote products derived from this software without specific
21 * prior written permission.
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include "portable_defns.h"
37 #include "ptst.h"
38 #include "gc.h"
39 #include <assert.h>
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <setjmp.h>
45 #include <signal.h>
47 typedef struct stm_blk_st stm_blk;
48 typedef struct stm_tx_entry_st stm_tx_entry;
49 typedef struct stm_tx_st stm_tx;
50 typedef struct stm_st stm;
52 struct stm_blk_st {
53 void *data;
54 mrsw_lock_t lock;
57 struct stm_tx_entry_st {
58 stm_blk *b;
59 void *old;
60 void *new;
61 stm_tx_entry *next;
64 struct stm_tx_st {
65 int status;
66 stm_tx_entry *blocks;
67 stm_tx_entry *alloc_ptr, *check;
68 int gc_data_id, blk_size; /* copied from 'stm' structure */
69 sigjmp_buf *penv;
72 struct stm_st {
73 int gc_data_id;
74 int blk_size;
77 #define DESCRIPTOR_IN_USE(_t) ((_t)->penv != NULL)
79 #define DESCRIPTOR_SIZE 4096
80 #define MAX_TX_ENTS (DESCRIPTOR_SIZE / sizeof(stm_tx_entry))
82 /* Private per-thread state. The array is indexed off ptst->id. */
83 typedef struct {
84 char desc[DESCRIPTOR_SIZE];
85 } priv_t;
87 static priv_t priv_ptst[MAX_THREADS];
88 static int gc_blk_id; /* Allocation id for block descriptors. */
89 static int do_padding; /* Should all allocations be padded to a cache line? */
91 #define ALLOCATOR_SIZE(_s) (do_padding ? CACHE_LINE_SIZE : (_s))
93 #define TXS_IN_PROGRESS 0
94 #define TXS_FAILED 1
95 #define TXS_SUCCESSFUL 2
97 bool_t commit_stm_tx(ptst_t *ptst, stm_tx *t);
99 static stm_tx_entry *alloc_stm_tx_entry(stm_tx *t)
101 stm_tx_entry *ent = t->alloc_ptr++;
102 assert(((unsigned long)t->alloc_ptr - (unsigned long)t) <=
103 DESCRIPTOR_SIZE);
104 return ent;
108 static stm_tx_entry **search_stm_tx_entry(stm_tx_entry **pnext, stm_blk *b)
110 stm_tx_entry *next = *pnext;
112 while ( (next != NULL) && ((unsigned long)next->b < (unsigned long)b) )
114 pnext = &next->next;
115 next = *pnext;
118 return pnext;
122 stm *new_stm(ptst_t *ptst, int blk_size)
124 stm *mem = malloc(CACHE_LINE_SIZE);
125 mem->blk_size = blk_size;
126 mem->gc_data_id = gc_add_allocator(ALLOCATOR_SIZE(blk_size));
127 return mem;
131 void free_stm(ptst_t *ptst, stm *mem)
133 gc_remove_allocator(mem->gc_data_id);
134 free(mem);
138 stm_blk *new_stm_blk(ptst_t *ptst, stm *mem)
140 stm_blk *b;
141 b = gc_alloc(ptst, gc_blk_id);
142 b->data = gc_alloc(ptst, mem->gc_data_id);
143 mrsw_init(&b->lock);
144 return b;
148 void free_stm_blk(ptst_t *ptst, stm *mem, stm_blk *b)
150 gc_free(ptst, b->data, mem->gc_data_id);
151 gc_free(ptst, b, gc_blk_id);
155 void *init_stm_blk(ptst_t *ptst, stm *mem, stm_blk *b)
157 return b->data;
161 int sizeof_stm_blk(ptst_t *ptst, stm *mem, stm_blk *b)
163 return mem->blk_size;
167 stm_tx *new_stm_tx(ptst_t *ptst, stm *mem, sigjmp_buf *penv)
169 stm_tx *t = (stm_tx *)&priv_ptst[ptst->id];
170 if ( DESCRIPTOR_IN_USE(t) ) goto nesting;
171 t->status = TXS_IN_PROGRESS;
172 t->blocks = NULL;
173 t->alloc_ptr = t->check = (stm_tx_entry *)(t + 1);
174 t->gc_data_id = mem->gc_data_id;
175 t->blk_size = mem->blk_size;
176 t->penv = penv;
177 return t;
179 nesting:
180 fprintf(stderr, "No nesting of transactions is allowed\n");
181 return NULL;
185 bool_t commit_stm_tx(ptst_t *ptst, stm_tx *t)
187 stm_tx_entry *ent, *last_ent;
188 mrsw_qnode_t qn[MAX_TX_ENTS];
189 stm_blk *b;
190 void *old;
191 int i;
193 t->penv = NULL;
195 /* Outcome may have been decided by an 'abort' or 'validate' operation. */
196 if ( t->status != TXS_IN_PROGRESS ) goto out;
198 /* We start by taking locks in order, and checking old values. */
199 for ( i = 0, ent = t->blocks; ent != NULL; i++, ent = ent->next )
201 b = ent->b;
202 if ( (old = ent->old) == ent->new )
204 rd_lock(&b->lock, &qn[i]);
206 else
208 wr_lock(&b->lock, &qn[i]);
210 /* Check old value, and shortcut to failure if we mismatch. */
211 if ( b->data != old ) goto fail;
215 * LINEARISATION POINT FOR SUCCESS:
216 * We haven't written new values yet, but that's okay as we have write
217 * locks on those locations. Noone can see old value now and yet still
218 * commit (as they'll be waiting for the read lock).
220 t->status = TXS_SUCCESSFUL;
222 /* We definitely succeed now: release locks and write new values. */
223 for ( i = 0, ent = t->blocks; ent != NULL; i++, ent = ent->next )
225 b = ent->b;
226 if ( ent->old == ent->new )
228 rd_unlock(&b->lock, &qn[i]);
230 else
232 b->data = ent->new;
233 wr_unlock(&b->lock, &qn[i]);
237 out:
238 if ( t->status == TXS_SUCCESSFUL )
240 for ( ent = t->blocks; ent != NULL; ent = ent->next )
242 if ( ent->old == ent->new ) continue;
243 gc_free(ptst, ent->old, t->gc_data_id);
245 return TRUE;
247 else
249 for ( ent = t->blocks; ent != NULL; ent = ent->next )
251 if ( ent->old == ent->new ) continue;
252 gc_unsafe_free(ptst, ent->new, t->gc_data_id);
254 return FALSE;
258 * We put (hopefully rare) failure case out-of-line here.
259 * This is also the LINEARISTAION POINT FOR FAILURE.
261 fail:
262 last_ent = ent->next;
263 t->status = TXS_FAILED;
264 for ( i = 0, ent = t->blocks; ent != last_ent; i++, ent = ent->next )
266 b = ent->b;
267 if ( ent->old == ent->new )
269 rd_unlock(&b->lock, &qn[i]);
271 else
273 wr_unlock(&b->lock, &qn[i]);
276 goto out;
280 bool_t validate_stm_tx(ptst_t *ptst, stm_tx *t)
282 stm_tx_entry *ent, *last_ent = NULL;
283 mrsw_qnode_t qn[MAX_TX_ENTS];
284 stm_blk *b;
285 void *old;
286 int i;
288 RMB();
290 /* Lock-acquire phase */
291 for ( i = 0, ent = t->blocks; ent != NULL; i++, ent = ent->next )
293 b = ent->b;
295 if ( (old = ent->old) == ent->new )
297 rd_lock(&b->lock, &qn[i]);
299 else
301 wr_lock(&b->lock, &qn[i]);
304 if ( b->data != old )
306 t->status = TXS_FAILED;
307 last_ent = ent->next;
308 break;
312 /* Lock-release phase */
313 for ( i = 0, ent = t->blocks; ent != last_ent; i++, ent = ent->next )
315 b = ent->b;
316 if ( ent->old == ent->new )
318 rd_unlock(&b->lock, &qn[i]);
320 else
322 wr_unlock(&b->lock, &qn[i]);
326 return t->status != TXS_FAILED;
330 void abort_stm_tx(ptst_t *ptst, stm_tx *t)
332 t->status = TXS_FAILED;
336 void *read_stm_blk(ptst_t *ptst, stm_tx *t, stm_blk *b)
338 stm_tx_entry **pent, *ent;
339 sigjmp_buf *penv;
340 void *result;
342 pent = search_stm_tx_entry(&t->blocks, b);
343 ent = *pent;
344 if ( (ent != NULL) && (ent->b == b) ) goto found;
346 ent = alloc_stm_tx_entry(t);
347 ent->b = b;
348 ent->old = b->data;
349 ent->new = ent->old;
350 ent->next = *pent;
351 *pent = ent;
352 return ent->new;
354 found:
355 result = ent->new;
356 ent = t->check;
357 if ( ent->b->data != ent->old ) goto fail;
358 if ( ++t->check == t->alloc_ptr ) t->check = (stm_tx_entry *)(t + 1);
359 return result;
361 fail:
362 penv = t->penv;
363 abort_stm_tx(ptst, t);
364 commit_stm_tx(ptst, t);
365 siglongjmp(*penv, 0);
366 assert(0);
367 return NULL;
371 void *write_stm_blk(ptst_t *ptst, stm_tx *t, stm_blk *b)
373 stm_tx_entry **pent, *ent;
374 sigjmp_buf *penv;
375 void *result;
377 pent = search_stm_tx_entry(&t->blocks, b);
378 ent = *pent;
379 if ( (ent != NULL) && (ent->b == b) )
381 if ( ent->old != ent->new ) goto found;
383 else
385 ent = alloc_stm_tx_entry(t);
386 ent->b = b;
387 ent->old = b->data;
388 ent->next = *pent;
389 *pent = ent;
392 ent->new = gc_alloc(ptst, t->gc_data_id);
393 memcpy(ent->new, ent->old, t->blk_size);
394 return ent->new;
396 found:
397 result = ent->new;
398 ent = t->check;
399 if ( ent->b->data != ent->old ) goto fail;
400 if ( ++t->check == t->alloc_ptr ) t->check = (stm_tx_entry *)(t + 1);
401 return result;
403 fail:
404 penv = t->penv;
405 abort_stm_tx(ptst, t);
406 commit_stm_tx(ptst, t);
407 siglongjmp(*penv, 0);
408 assert(0);
409 return NULL;
413 void remove_from_tx(ptst_t *ptst, stm_tx *t, stm_blk *b)
415 stm_tx_entry **pent, *ent;
416 void *data;
418 pent = search_stm_tx_entry(&t->blocks, b);
419 ent = *pent;
420 if ( (ent != NULL) && (ent->b == b) )
422 *pent = ent->next;
423 if ( (data = ent->new) != ent->old )
425 gc_free(ptst, data, t->gc_data_id);
431 static void handle_fault(int sig)
433 ptst_t *ptst;
434 stm_tx *t;
436 ptst = critical_enter();
437 t = (stm_tx *)&priv_ptst[ptst->id];
438 if ( DESCRIPTOR_IN_USE(t) && !validate_stm_tx(ptst, t) )
440 sigjmp_buf *penv = t->penv;
441 commit_stm_tx(ptst, t);
442 critical_exit(ptst);
443 siglongjmp(*penv, 0);
446 fail:
447 fprintf(stderr, "Error: unhandleable SIGSEGV!\n");
448 abort();
452 void _init_stm_subsystem(int pad_data)
454 struct sigaction act;
456 do_padding = pad_data;
457 gc_blk_id = gc_add_allocator(ALLOCATOR_SIZE(sizeof(stm_blk)));
458 memset(priv_ptst, 0, sizeof(priv_ptst));
460 act.sa_handler = handle_fault;
461 sigemptyset(&act.sa_mask);
462 act.sa_flags = 0;
463 sigaction(SIGSEGV, &act, NULL);