1 /******************************************************************************
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
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"
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
;
57 struct stm_tx_entry_st
{
67 stm_tx_entry
*alloc_ptr
, *check
;
68 int gc_data_id
, blk_size
; /* copied from 'stm' structure */
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. */
84 char desc
[DESCRIPTOR_SIZE
];
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
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
) <=
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
) )
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
));
131 void free_stm(ptst_t
*ptst
, stm
*mem
)
133 gc_remove_allocator(mem
->gc_data_id
);
138 stm_blk
*new_stm_blk(ptst_t
*ptst
, stm
*mem
)
141 b
= gc_alloc(ptst
, gc_blk_id
);
142 b
->data
= gc_alloc(ptst
, mem
->gc_data_id
);
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
)
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
;
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
;
180 fprintf(stderr
, "No nesting of transactions is allowed\n");
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
];
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
)
202 if ( (old
= ent
->old
) == ent
->new )
204 rd_lock(&b
->lock
, &qn
[i
]);
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
)
226 if ( ent
->old
== ent
->new )
228 rd_unlock(&b
->lock
, &qn
[i
]);
233 wr_unlock(&b
->lock
, &qn
[i
]);
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
);
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
);
258 * We put (hopefully rare) failure case out-of-line here.
259 * This is also the LINEARISTAION POINT FOR FAILURE.
262 last_ent
= ent
->next
;
263 t
->status
= TXS_FAILED
;
264 for ( i
= 0, ent
= t
->blocks
; ent
!= last_ent
; i
++, ent
= ent
->next
)
267 if ( ent
->old
== ent
->new )
269 rd_unlock(&b
->lock
, &qn
[i
]);
273 wr_unlock(&b
->lock
, &qn
[i
]);
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
];
290 /* Lock-acquire phase */
291 for ( i
= 0, ent
= t
->blocks
; ent
!= NULL
; i
++, ent
= ent
->next
)
295 if ( (old
= ent
->old
) == ent
->new )
297 rd_lock(&b
->lock
, &qn
[i
]);
301 wr_lock(&b
->lock
, &qn
[i
]);
304 if ( b
->data
!= old
)
306 t
->status
= TXS_FAILED
;
307 last_ent
= ent
->next
;
312 /* Lock-release phase */
313 for ( i
= 0, ent
= t
->blocks
; ent
!= last_ent
; i
++, ent
= ent
->next
)
316 if ( ent
->old
== ent
->new )
318 rd_unlock(&b
->lock
, &qn
[i
]);
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
;
342 pent
= search_stm_tx_entry(&t
->blocks
, b
);
344 if ( (ent
!= NULL
) && (ent
->b
== b
) ) goto found
;
346 ent
= alloc_stm_tx_entry(t
);
357 if ( ent
->b
->data
!= ent
->old
) goto fail
;
358 if ( ++t
->check
== t
->alloc_ptr
) t
->check
= (stm_tx_entry
*)(t
+ 1);
363 abort_stm_tx(ptst
, t
);
364 commit_stm_tx(ptst
, t
);
365 siglongjmp(*penv
, 0);
371 void *write_stm_blk(ptst_t
*ptst
, stm_tx
*t
, stm_blk
*b
)
373 stm_tx_entry
**pent
, *ent
;
377 pent
= search_stm_tx_entry(&t
->blocks
, b
);
379 if ( (ent
!= NULL
) && (ent
->b
== b
) )
381 if ( ent
->old
!= ent
->new ) goto found
;
385 ent
= alloc_stm_tx_entry(t
);
392 ent
->new = gc_alloc(ptst
, t
->gc_data_id
);
393 memcpy(ent
->new, ent
->old
, t
->blk_size
);
399 if ( ent
->b
->data
!= ent
->old
) goto fail
;
400 if ( ++t
->check
== t
->alloc_ptr
) t
->check
= (stm_tx_entry
*)(t
+ 1);
405 abort_stm_tx(ptst
, t
);
406 commit_stm_tx(ptst
, t
);
407 siglongjmp(*penv
, 0);
413 void remove_from_tx(ptst_t
*ptst
, stm_tx
*t
, stm_blk
*b
)
415 stm_tx_entry
**pent
, *ent
;
418 pent
= search_stm_tx_entry(&t
->blocks
, b
);
420 if ( (ent
!= NULL
) && (ent
->b
== b
) )
423 if ( (data
= ent
->new) != ent
->old
)
425 gc_free(ptst
, data
, t
->gc_data_id
);
431 static void handle_fault(int sig
)
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
);
443 siglongjmp(*penv
, 0);
447 fprintf(stderr
, "Error: unhandleable SIGSEGV!\n");
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
);
463 sigaction(SIGSEGV
, &act
, NULL
);