1 /* ============================================================
3 * - single thread commit on local machine
4 * =============================================================
5 * Copyright (c) 2009, University of California, Irvine, USA.
9 * =============================================================
16 /* Per thread transaction variables */
17 __thread objstr_t *t_cache;
18 __thread objstr_t *t_reserve;
19 __thread struct objlist * newobjs;
22 #include "delaycomp.h"
23 __thread struct pointerlist ptrstack;
24 __thread struct primitivelist primstack;
25 __thread struct branchlist branchstack;
26 struct pointerlist *c_ptrstack;
27 struct primitivelist *c_primstack;
28 struct branchlist *c_branchstack;
32 int numTransCommit = 0;
33 int numTransAbort = 0;
35 int nSoftAbortCommit = 0;
36 int nSoftAbortAbort = 0;
44 gettimeofday(&t, NULL);
46 (int)t.tv_sec * 1000000 +
50 /* Thread variable for locking/unlocking */
51 __thread threadrec_t *trec;
52 __thread struct objlist * lockedobjs;
53 __thread int t_objnumcount=0;
55 /* Collect stats for object classes causing abort */
56 objtypestat_t typesCausingAbort[TOTALNUMCLASSANDARRAY];
57 /******Keep track of objects and types causing aborts******/
58 /* TODO uncomment for later use
59 #define DEBUGSTMSTAT(args...) { \
66 * Inline fuction to get Transaction size per object type for those
70 INLINE void getTransSize(objheader_t *header , int *isObjTypeTraverse) {
71 (typesCausingAbort[TYPE(header)]).numabort++;
72 if(isObjTypeTraverse[TYPE(header)] != 1) {
73 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
74 (typesCausingAbort[TYPE(header)]).numtrans+=1; //should this count be kept per object
76 isObjTypeTraverse[TYPE(header)]=1;
78 #define DEBUGSTMSTAT(args...)
80 #define DEBUGSTMSTAT(args...)
84 #define DEBUGSTM(x...) printf(x);
86 #define DEBUGSTM(x...);
90 #define DEBUGSTATS(x...) printf(x);
92 #define DEBUGSTATS(x...);
96 //void * A_memcpy (void * dest, const void * src, size_t count);
98 //#define A_memcpy memcpy
101 void * A_memcpy (void * dest, const void * src, size_t count) {
103 INTPTR *desti=(INTPTR *)dest;
104 INTPTR *srci=(INTPTR *)src;
107 while(count>=sizeof(INTPTR)) {
108 desti[off]=srci[off];
110 count-=sizeof(INTPTR);
115 ((char *)dest)[off]=((char *)src)[off];
122 extern void * curr_heapbase;
123 extern void * curr_heapptr;
124 extern void * curr_heaptop;
127 /*** Global variables *****/
128 objlockstate_t *objlockscope;
131 * params: object header
132 * Increments the abort count for each object
134 void ABORTCOUNT(objheader_t * x) {
135 int avgTransSize = typesCausingAbort[TYPE(x)].numaccess / typesCausingAbort[TYPE(x)].numtrans;
136 float transAbortProbForObj = (PERCENT_ALLOWED_ABORT*FACTOR)/(float) avgTransSize;
137 float ObjAbortProb = x->abortCount/(float) (x->accessCount);
138 DEBUGSTM("ABORTSTATS: oid= %x, type= %2d, transAbortProb= %2.2f, ObjAbortProb= %2.2f, Typenumaccess= %3d, avgtranssize = %2d, ObjabortCount= %2d, ObjaccessCount= %3d\n", OID(x), TYPE(x), transAbortProbForObj, ObjAbortProb, typesCausingAbort[TYPE(x)].numaccess, avgTransSize, x->abortCount, x->accessCount);
139 /* Condition for locking objects */
140 if (((ObjAbortProb*100) >= transAbortProbForObj) && (x->riskyflag != 1)) {
141 DEBUGSTATS("AFTER LOCK ABORTSTATS: oid= %x, type= %2d, transAbortProb= %2.2f, ObjAbortProb= %2.2f, Typenumaccess= %3d, avgtranssize = %2d, ObjabortCount= %2d, ObjaccessCount= %3d\n", OID(x), TYPE(x), transAbortProbForObj, ObjAbortProb, typesCausingAbort[TYPE(x)].numaccess, avgTransSize, x->abortCount, x->accessCount);
142 //makes riskflag sticky
143 pthread_mutex_lock(&lockedobjstore);
144 if (objlockscope->offset<MAXOBJLIST) {
145 x->objlock=&(objlockscope->lock[objlockscope->offset++]);
147 objlockstate_t *tmp=malloc(sizeof(objlockstate_t));
148 tmp->next=objlockscope;
150 x->objlock=&(tmp->lock[0]);
153 pthread_mutex_unlock(&lockedobjstore);
154 pthread_mutex_init(x->objlock, NULL);
155 //should put a memory barrier here
161 /* ==================================================
163 * This function starts up the transaction runtime.
164 * ==================================================
170 /* ======================================
172 * - create an object store of given size
173 * ======================================
175 objstr_t *objstrCreate(unsigned int size) {
177 if((tmp = calloc(1, (sizeof(objstr_t) + size))) == NULL) {
178 printf("%s() Calloc error at line %d, %s\n", __func__, __LINE__, __FILE__);
183 tmp->top = tmp + 1; //points to end of objstr_t structure!
188 while(t_cache->next!=NULL) {
189 objstr_t *next=t_cache->next;
190 t_cache->next=t_reserve;
194 t_cache->top=t_cache+1;
200 //free entire list, starting at store
201 void objstrDelete(objstr_t *store) {
203 while (store != NULL) {
211 /* =================================================
213 * This function initializes things required in the
215 * =================================================
218 //Transaction start is currently free...commit and aborting is not
220 c_ptrstack=&ptrstack;
221 c_primstack=&primstack;
222 c_branchstack=&branchstack;
226 /* =======================================================
228 * This function creates objects in the transaction record
229 * =======================================================
231 objheader_t *transCreateObj(void * ptr, unsigned int size) {
232 objheader_t *tmp = mygcmalloc(ptr, (sizeof(objheader_t) + size));
233 objheader_t *retval=&tmp[1];
234 tmp->lock=RW_LOCK_BIAS;
236 //initialize obj lock to the header
238 // don't insert into table
239 if (newobjs->offset<MAXOBJLIST) {
240 newobjs->objs[newobjs->offset++]=retval;
242 struct objlist *tmp=malloc(sizeof(struct objlist));
248 return retval; //want space after object header
251 /* This functions inserts randowm wait delays in the order of msec
252 * Mostly used when transaction commits retry*/
253 void randomdelay(int softaborted) {
257 gettimeofday(&t,NULL);
260 req.tv_nsec = (long)((t.tv_usec)%(1<<softaborted))<<1; //1-11 microsec
261 nanosleep(&req, NULL);
265 /* ==============================================
267 * - allocate space in an object store
268 * ==============================================
270 void *objstrAlloc(unsigned int size) {
273 objstr_t *store=t_cache;
279 if (OSFREE(store)>=size) {
284 if ((store=store->next)==NULL)
289 unsigned int newsize=size>DEFAULT_OBJ_STORE_SIZE ? size : DEFAULT_OBJ_STORE_SIZE;
290 objstr_t **otmp=&t_reserve;
292 while((ptr=*otmp)!=NULL) {
293 if (ptr->size>=newsize) {
298 ptr->top=((char *)(&ptr[1]))+size;
303 objstr_t *os=(objstr_t *)calloc(1,(sizeof(objstr_t) + newsize));
308 os->top=((char *)nptr)+size;
314 /* =============================================================
316 * -finds the objects either in main heap
317 * -copies the object into the transaction cache
318 * =============================================================
320 //__attribute__ ((pure))
321 void *transRead(void * oid, void *gl) {
322 objheader_t *tmp, *objheader;
323 objheader_t *objcopy;
326 /* Read from the main heap */
328 objheader_t *header = (objheader_t *)(((char *)oid) - sizeof(objheader_t));
329 GETSIZE(size, header);
330 size += sizeof(objheader_t);
331 objcopy = (objheader_t *) objstrAlloc(size);
333 header->accessCount++;
334 if(header->riskyflag) {
335 header=needLock(header,gl);
338 A_memcpy(objcopy, header, size);
340 /* keep track of the object's access sequence in a transaction */
341 objheader_t *tmpheader = objcopy;
342 tmpheader->accessCount = ++t_objnumcount;
345 /* Insert into cache's lookup table */
347 if (((unsigned INTPTR)oid)<((unsigned INTPTR ) curr_heapbase)|| ((unsigned INTPTR)oid) >((unsigned INTPTR) curr_heapptr))
348 printf("ERROR! Bad object address!\n");
349 t_chashInsert(oid, &objcopy[1]);
354 struct objlist *ptr=newobjs;
355 while(ptr->next!=NULL) {
356 struct objlist *tmp=ptr->next;
365 void freelockedobjs() {
366 struct objlist *ptr=lockedobjs;
367 while(ptr->next!=NULL) {
368 struct objlist *tmp=ptr->next;
377 /* ================================================================
379 * - This function initiates the transaction commit process
380 * - goes through the transaction cache and decides
382 * ================================================================
385 int transCommit(void (*commitmethod)(void *, void *, void *), void * primitives, void * locals, void * params) {
394 /* Look through all the objects in the transaction hash table */
397 if (c_numelements<(c_size>>3))
398 finalResponse= alttraverseCache(commitmethod, primitives, locals, params);
400 finalResponse= traverseCache(commitmethod, primitives, locals, params);
402 if (c_numelements<(c_size>>3))
403 finalResponse= alttraverseCache();
405 finalResponse= traverseCache();
407 if(finalResponse == TRANS_ABORT) {
431 if(finalResponse == TRANS_COMMIT) {
456 /* wait a random amount of time before retrying to commit transaction*/
457 if(finalResponse == TRANS_SOFT_ABORT) {
467 //retry if too many soft aborts
485 //randomdelay(softaborted);
487 printf("Error: in %s() Unknown outcome", __func__);
494 #define freearrays if (c_numelements>=200) { \
496 free(oidrdversion); \
499 if (t_numelements>=200) { \
503 #define freearrays if (c_numelements>=200) { \
505 free(oidrdversion); \
512 #define allocarrays int t_numelements=c_numelements+dc_c_numelements; \
513 if (t_numelements<200) { \
514 oidwrlocked=wrlocked; \
516 oidwrlocked=malloc(t_numelements*sizeof(void *)); \
518 if (c_numelements<200) { \
519 oidrdlocked=rdlocked; \
520 oidrdversion=rdversion; \
523 int size=c_numelements*sizeof(void*); \
524 oidrdlocked=malloc(size); \
525 oidrdversion=malloc(size); \
526 oidrdage=malloc(size); \
529 #define allocarrays if (c_numelements<200) { \
530 oidrdlocked=rdlocked; \
531 oidrdversion=rdversion; \
533 oidwrlocked=wrlocked; \
535 int size=c_numelements*sizeof(void*); \
536 oidrdlocked=malloc(size); \
537 oidrdversion=malloc(size); \
538 oidwrlocked=malloc(size); \
539 oidrdage=malloc(size); \
546 /* ==================================================
548 * - goes through the transaction cache and
549 * - decides if a transaction should commit or abort
550 * ==================================================
553 int traverseCache(void (*commitmethod)(void *, void *, void *), void * primitives, void * locals, void * params) {
555 int traverseCache() {
557 /* Create info to keep track of objects that can be locked */
558 int numoidrdlocked=0;
559 int numoidwrlocked=0;
560 void * rdlocked[200];
563 void * wrlocked[200];
571 int objtypetraverse[TOTALNUMCLASSANDARRAY];
574 for(i=0; i<TOTALNUMCLASSANDARRAY; i++)
575 objtypetraverse[i] = 0;
577 chashlistnode_t *ptr = c_table;
578 /* Represents number of bins in the chash table */
579 unsigned int size = c_size;
580 for(i = 0; i<size; i++) {
581 chashlistnode_t *curr = &ptr[i];
582 /* Inner loop to traverse the linked list of the cache lookupTable */
583 while(curr != NULL) {
584 //if the first bin in hash table is empty
585 if(curr->key == NULL)
587 objheader_t * headeraddr=&((objheader_t *) curr->val)[-1]; //cached object
588 objheader_t *header=(objheader_t *)(((char *)curr->key)-sizeof(objheader_t)); //real object
589 unsigned int version = headeraddr->version;
591 if(STATUS(headeraddr) & DIRTY) {
592 /* Read from the main heap and compare versions */
593 if(write_trylock(&header->lock)) { //can aquire write lock
594 if (version == header->version) { /* versions match */
595 /* Keep track of objects locked */
596 oidwrlocked[numoidwrlocked++] = header;
598 oidwrlocked[numoidwrlocked++] = header;
599 transAbortProcess(oidwrlocked, numoidwrlocked);
601 header->abortCount++;
602 ObjSeqId = headeraddr->accessCount;
603 (typesCausingAbort[TYPE(header)]).numabort++;
604 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
605 (typesCausingAbort[TYPE(header)]).numtrans+=1;
606 objtypetraverse[TYPE(header)]=1;
607 getTotalAbortCount(i+1, size, (void *)(curr->next), numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse);
609 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version);
610 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
613 return TRANS_SOFT_ABORT;
618 if(version == header->version) {
622 transAbortProcess(oidwrlocked, numoidwrlocked);
624 header->abortCount++;
625 ObjSeqId = headeraddr->accessCount;
626 (typesCausingAbort[TYPE(header)]).numabort++;
627 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
628 (typesCausingAbort[TYPE(header)]).numtrans+=1;
629 objtypetraverse[TYPE(header)]=1;
630 //(typesCausingAbort[TYPE(header)])++;
632 #if defined(STMSTATS)||defined(SOFTABORT)
633 if(getTotalAbortCount(i+1, size, (void *)(curr->next), numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse))
636 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version);
637 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
640 return TRANS_SOFT_ABORT;
647 oidrdversion[numoidrdlocked]=version;
648 oidrdlocked[numoidrdlocked]=header;
649 oidrdage[numoidrdlocked++]=headeraddr->accessCount;
651 oidrdversion[numoidrdlocked]=version;
652 oidrdlocked[numoidrdlocked++]=header;
660 //acquire access set locks
661 unsigned int numoidwrtotal=numoidwrlocked;
663 chashlistnode_t *dc_curr = dc_c_list;
664 /* Inner loop to traverse the linked list of the cache lookupTable */
665 while(likely(dc_curr != NULL)) {
666 //if the first bin in hash table is empty
667 objheader_t * headeraddr=&((objheader_t *) dc_curr->val)[-1];
668 objheader_t *header=(objheader_t *)(((char *)dc_curr->key)-sizeof(objheader_t));
669 if(write_trylock(&header->lock)) { //can aquire write lock
670 oidwrlocked[numoidwrtotal++] = header;
672 //maybe we already have lock
673 void * key=dc_curr->key;
674 chashlistnode_t *node = &c_table[(((unsigned INTPTR)key) & c_mask)>>4];
677 if(node->key == key) {
678 objheader_t * headeraddr=&((objheader_t *) node->val)[-1];
679 if(STATUS(headeraddr) & DIRTY) {
685 } while(node != NULL);
687 //have to abort to avoid deadlock
688 transAbortProcess(oidwrlocked, numoidwrtotal);
690 ObjSeqId = headeraddr->accessCount;
691 header->abortCount++;
692 (typesCausingAbort[TYPE(header)]).numabort++;
693 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
694 (typesCausingAbort[TYPE(header)]).numtrans+=1;
695 objtypetraverse[TYPE(header)]=1;
697 #if defined(STMSTATS)||defined(SOFTABORT)
698 if(getTotalAbortCount(i+1, size, (void *)(curr->next), numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse))
701 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version);
702 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
705 return TRANS_SOFT_ABORT;
710 dc_curr = dc_curr->lnext;
714 //THIS IS THE SERIALIZATION END POINT (START POINT IS END OF EXECUTION)*****
716 for(i=0; i<numoidrdlocked; i++) {
717 /* Read from the main heap and compare versions */
718 objheader_t *header=oidrdlocked[i];
719 unsigned int version=oidrdversion[i];
720 if(header->lock>0) { //not write locked
722 if(version != header->version) { /* versions do not match */
724 transAbortProcess(oidwrlocked, numoidwrtotal);
726 transAbortProcess(oidwrlocked, numoidwrlocked);
729 ObjSeqId = oidrdage[i];
730 header->abortCount++;
731 (typesCausingAbort[TYPE(header)]).numabort++;
732 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
733 (typesCausingAbort[TYPE(header)]).numtrans+=1;
734 objtypetraverse[TYPE(header)]=1;
735 getReadAbortCount(i+1, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse);
737 DEBUGSTM("RD Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
738 DEBUGSTMSTAT("RD Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
743 } else if (dc_t_chashSearch(((char *)header)+sizeof(objheader_t))!=NULL) {
744 //couldn't get lock because we already have it
745 //check if it is the right version number
746 if (version!=header->version) {
747 transAbortProcess(oidwrlocked, numoidwrtotal);
749 ObjSeqId = oidrdage[i];
750 header->abortCount++;
751 (typesCausingAbort[TYPE(header)]).numabort++;
752 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
753 (typesCausingAbort[TYPE(header)]).numtrans+=1;
754 objtypetraverse[TYPE(header)]=1;
755 getReadAbortCount(i+1, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse);
757 DEBUGSTM("RD Abort: rd: %u wr: %u tot: %u type: %u ver: %u, oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
758 DEBUGSTMSTAT("RD Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
763 } else { /* cannot aquire lock */
764 //do increment as we didn't get lock
765 if(version == header->version) {
769 transAbortProcess(oidwrlocked, numoidwrtotal);
771 transAbortProcess(oidwrlocked, numoidwrlocked);
774 ObjSeqId = oidrdage[i];
775 header->abortCount++;
776 (typesCausingAbort[TYPE(header)]).numabort++;
777 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
778 (typesCausingAbort[TYPE(header)]).numtrans+=1;
779 objtypetraverse[TYPE(header)]=1;
781 #if defined(STMSTATS)||defined(SOFTABORT)
782 if(getReadAbortCount(i+1, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse))
785 DEBUGSTM("RD Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
786 DEBUGSTMSTAT("RD Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
789 return TRANS_SOFT_ABORT;
796 //need to validate auxilary readset
797 rdchashlistnode_t *rd_curr = rd_c_list;
798 /* Inner loop to traverse the linked list of the cache lookupTable */
799 while(likely(rd_curr != NULL)) {
800 //if the first bin in hash table is empty
801 unsigned int version=rd_curr->version;
802 objheader_t *header=(objheader_t *)(((char *)rd_curr->key)-sizeof(objheader_t));
803 if(header->lock>0) { //object is not locked
804 if (version!=header->version) {
807 transAbortProcess(oidwrlocked, numoidwrtotal);
809 transAbortProcess(oidwrlocked, numoidwrlocked);
812 //ABORTCOUNT(header);
813 (typesCausingAbort[TYPE(header)])++;
815 #if defined(STMSTATS)||defined(SOFTABORT)
816 //if(getTotalAbortCount2((void *) curr->next, numoidrdlocked, oidrdlocked, oidrdversion))
819 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
820 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
823 return TRANS_SOFT_ABORT;
828 //maybe we already have lock
829 if (version==header->version) {
830 void * key=rd_curr->key;
832 //check to see if it is in the delaycomp table
834 chashlistnode_t *node = &dc_c_table[(((unsigned INTPTR)key) & dc_c_mask)>>4];
839 } while(node != NULL);
844 chashlistnode_t *node = &c_table[(((unsigned INTPTR)key) & c_mask)>>4];
846 if(node->key == key) {
847 objheader_t * headeraddr=&((objheader_t *) node->val)[-1];
848 if(STATUS(headeraddr) & DIRTY) {
853 } while(node != NULL);
857 //have to abort to avoid deadlock
858 transAbortProcess(oidwrlocked, numoidwrtotal);
860 transAbortProcess(oidwrlocked, numoidwrlocked);
864 //ABORTCOUNT(header);
865 (typesCausingAbort[TYPE(header)])++;
867 #if defined(STMSTATS)||defined(SOFTABORT)
868 //if(getTotalAbortCount2((void *) curr->next, numoidrdlocked, oidrdlocked, oidrdversion))
871 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
872 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
875 return TRANS_SOFT_ABORT;
880 rd_curr = rd_curr->lnext;
884 /* Decide the final response */
886 transCommitProcess(oidwrlocked, numoidwrlocked, numoidwrtotal, commitmethod, primitives, locals, params);
888 transCommitProcess(oidwrlocked, numoidwrlocked);
890 DEBUGSTM("Commit: rd: %u wr: %u tot: %u\n", numoidrdlocked, numoidwrlocked, c_numelements);
895 /* ==================================================
897 * - goes through the transaction cache and
898 * - decides if a transaction should commit or abort
899 * ==================================================
903 int alttraverseCache(void (*commitmethod)(void *, void *, void *), void * primitives, void * locals, void * params) {
905 int alttraverseCache() {
907 /* Create info to keep track of objects that can be locked */
908 int numoidrdlocked=0;
909 int numoidwrlocked=0;
910 void * rdlocked[200];
913 void * wrlocked[200];
922 int objtypetraverse[TOTALNUMCLASSANDARRAY];
924 for(i=0; i<TOTALNUMCLASSANDARRAY; i++)
925 objtypetraverse[i] = 0;
926 chashlistnode_t *curr = c_list;
927 /* Inner loop to traverse the linked list of the cache lookupTable */
928 while(likely(curr != NULL)) {
929 //if the first bin in hash table is empty
930 objheader_t * headeraddr=&((objheader_t *) curr->val)[-1];
931 objheader_t *header=(objheader_t *)(((char *)curr->key)-sizeof(objheader_t));
932 unsigned int version = headeraddr->version;
934 if(STATUS(headeraddr) & DIRTY) {
935 /* Read from the main heap and compare versions */
936 if(likely(write_trylock(&header->lock))) { //can aquire write lock
937 if (likely(version == header->version)) { /* versions match */
938 /* Keep track of objects locked */
939 oidwrlocked[numoidwrlocked++] = header;
941 oidwrlocked[numoidwrlocked++] = header;
942 transAbortProcess(oidwrlocked, numoidwrlocked);
944 header->abortCount++;
945 ObjSeqId = headeraddr->accessCount;
946 (typesCausingAbort[TYPE(header)]).numabort++;
947 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
948 (typesCausingAbort[TYPE(header)]).numtrans+=1;
949 objtypetraverse[TYPE(header)]=1;
950 getTotalAbortCount2((void *) curr->next, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse);
952 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
953 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
957 } else { /* cannot aquire lock */
958 if(version == header->version) {
962 transAbortProcess(oidwrlocked, numoidwrlocked);
964 header->abortCount++;
965 ObjSeqId = headeraddr->accessCount;
966 (typesCausingAbort[TYPE(header)]).numabort++;
967 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
968 (typesCausingAbort[TYPE(header)]).numtrans+=1;
969 objtypetraverse[TYPE(header)]=1;
971 #if defined(STMSTATS)||defined(SOFTABORT)
972 if(getTotalAbortCount2((void *) curr->next, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse))
975 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
976 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
979 return TRANS_SOFT_ABORT;
984 /* Read from the main heap and compare versions */
985 oidrdversion[numoidrdlocked]=version;
986 oidrdlocked[numoidrdlocked++] = header;
992 //acquire other locks
993 unsigned int numoidwrtotal=numoidwrlocked;
994 chashlistnode_t *dc_curr = dc_c_list;
995 /* Inner loop to traverse the linked list of the cache lookupTable */
996 while(likely(dc_curr != NULL)) {
997 //if the first bin in hash table is empty
998 objheader_t * headeraddr=&((objheader_t *) dc_curr->val)[-1];
999 objheader_t *header=(objheader_t *)(((char *)dc_curr->key)-sizeof(objheader_t));
1000 if(write_trylock(&header->lock)) { //can aquire write lock
1001 oidwrlocked[numoidwrtotal++] = header;
1003 //maybe we already have lock
1004 void * key=dc_curr->key;
1005 chashlistnode_t *node = &c_table[(((unsigned INTPTR)key) & c_mask)>>4];
1008 if(node->key == key) {
1009 objheader_t * headeraddr=&((objheader_t *) node->val)[-1];
1010 if(STATUS(headeraddr) & DIRTY) {
1015 } while(node != NULL);
1017 //have to abort to avoid deadlock
1018 transAbortProcess(oidwrlocked, numoidwrtotal);
1020 header->abortCount++;
1021 ObjSeqId = headeraddr->accessCount;
1022 (typesCausingAbort[TYPE(header)]).numabort++;
1023 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
1024 (typesCausingAbort[TYPE(header)]).numtrans+=1;
1025 objtypetraverse[TYPE(header)]=1;
1027 #if defined(STMSTATS)||defined(SOFTABORT)
1028 if(getTotalAbortCount2((void *) curr->next, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse))
1031 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
1032 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
1035 return TRANS_SOFT_ABORT;
1040 dc_curr = dc_curr->lnext;
1044 //THIS IS THE SERIALIZATION END POINT (START POINT IS END OF EXECUTION)*****
1046 for(i=0; i<numoidrdlocked; i++) {
1047 objheader_t * header=oidrdlocked[i];
1048 unsigned int version=oidrdversion[i];
1049 if(header->lock>0) {
1051 if(version != header->version) {
1053 transAbortProcess(oidwrlocked, numoidwrtotal);
1055 transAbortProcess(oidwrlocked, numoidwrlocked);
1058 ObjSeqId = oidrdage[i];
1059 header->abortCount++;
1060 (typesCausingAbort[TYPE(header)]).numabort++;
1061 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
1062 (typesCausingAbort[TYPE(header)]).numtrans+=1;
1063 objtypetraverse[TYPE(header)]=1;
1064 getReadAbortCount(i+1, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse);
1066 DEBUGSTM("RD Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
1067 DEBUGSTMSTAT("RD Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
1072 } else if (dc_t_chashSearch(((char *)header)+sizeof(objheader_t))!=NULL) {
1073 //couldn't get lock because we already have it
1074 //check if it is the right version number
1075 if (version!=header->version) {
1076 transAbortProcess(oidwrlocked, numoidwrtotal);
1078 ObjSeqId = oidrdage[i];
1079 header->abortCount++;
1080 getReadAbortCount(i+1, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse);
1082 DEBUGSTM("RD Abort: rd: %u wr: %u tot: %u type: %u ver: %u oid: %x\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version, OID(header));
1083 DEBUGSTMSTAT("RD Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
1088 } else { /* cannot aquire lock */
1089 if(version == header->version) {
1093 transAbortProcess(oidwrlocked, numoidwrtotal);
1095 transAbortProcess(oidwrlocked, numoidwrlocked);
1098 ObjSeqId = oidrdage[i];
1099 header->abortCount++;
1100 (typesCausingAbort[TYPE(header)]).numabort++;
1101 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
1102 (typesCausingAbort[TYPE(header)]).numtrans+=1;
1103 objtypetraverse[TYPE(header)]=1;
1105 #if defined(STMSTATS)||defined(SOFTABORT)
1106 if(getReadAbortCount(i+1, numoidrdlocked, oidrdlocked, oidrdversion, oidrdage, ObjSeqId, header, objtypetraverse))
1109 DEBUGSTM("RD Abort: rd: %u wr: %u tot: %u type: %u ver: %u\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version);
1110 DEBUGSTMSTAT("RD Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
1113 return TRANS_SOFT_ABORT;
1120 //need to validate auxilary readset
1121 rdchashlistnode_t *rd_curr = rd_c_list;
1122 /* Inner loop to traverse the linked list of the cache lookupTable */
1123 while(likely(rd_curr != NULL)) {
1124 //if the first bin in hash table is empty
1125 int version=rd_curr->version;
1126 objheader_t *header=(objheader_t *)(((char *)rd_curr->key)-sizeof(objheader_t));
1127 if(header->lock>0) { //object is not locked
1128 if (version!=header->version) {
1131 transAbortProcess(oidwrlocked, numoidwrtotal);
1133 transAbortProcess(oidwrlocked, numoidwrlocked);
1136 //ABORTCOUNT(header);
1137 (typesCausingAbort[TYPE(header)])++;
1139 #if defined(STMSTATS)||defined(SOFTABORT)
1140 //if(getTotalAbortCount2((void *) curr->next, numoidrdlocked, oidrdlocked, oidrdversion))
1143 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version);
1144 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
1147 return TRANS_SOFT_ABORT;
1152 if (version==header->version) {
1153 void * key=rd_curr->key;
1155 //check to see if it is in the delaycomp table
1157 chashlistnode_t *node = &dc_c_table[(((unsigned INTPTR)key) & dc_c_mask)>>4];
1159 if(node->key == key)
1162 } while(node != NULL);
1165 //check normal table
1167 chashlistnode_t *node = &c_table[(((unsigned INTPTR)key) & c_mask)>>4];
1169 if(node->key == key) {
1170 objheader_t * headeraddr=&((objheader_t *) node->val)[-1];
1171 if(STATUS(headeraddr) & DIRTY) {
1176 } while(node != NULL);
1180 //have to abort to avoid deadlock
1181 transAbortProcess(oidwrlocked, numoidwrtotal);
1183 transAbortProcess(oidwrlocked, numoidwrlocked);
1186 //ABORTCOUNT(header);
1187 (typesCausingAbort[TYPE(header)])++;
1189 #if defined(STMSTATS)||defined(SOFTABORT)
1190 // if(getTotalAbortCount2((void *) curr->next, numoidrdlocked, oidrdlocked, oidrdversion))
1193 DEBUGSTM("WR Abort: rd: %u wr: %u tot: %u type: %u ver: %u\n", numoidrdlocked, numoidwrlocked, c_numelements, TYPE(header), header->version);
1194 DEBUGSTMSTAT("WR Abort: Access Count: %u AbortCount: %u type: %u ver: %u \n", header->accessCount, header->abortCount, TYPE(header), header->version);
1197 return TRANS_SOFT_ABORT;
1202 rd_curr = rd_curr->lnext;
1206 /* Decide the final response */
1208 transCommitProcess(oidwrlocked, numoidwrlocked, numoidwrtotal, commitmethod, primitives, locals, params);
1210 transCommitProcess(oidwrlocked, numoidwrlocked);
1212 DEBUGSTM("Commit: rd: %u wr: %u tot: %u\n", numoidrdlocked, numoidwrlocked, c_numelements);
1214 return TRANS_COMMIT;
1217 /* ==================================
1220 * =================================
1222 void transAbortProcess(void **oidwrlocked, int numoidwrlocked) {
1224 objheader_t *header;
1225 /* Release read locks */
1227 /* Release write locks */
1228 for(i=numoidwrlocked-1; i>=0; i--) {
1229 /* Read from the main heap */
1230 header = (objheader_t *)oidwrlocked[i];
1231 write_unlock(&header->lock);
1235 /* clear trec and then release objects locked */
1236 struct objlist *ptr=lockedobjs;
1238 int max=ptr->offset;
1239 for(i=max-1; i>=0; i--) {
1240 header = (objheader_t *)ptr->objs[i];
1241 header->trec = NULL;
1242 pthread_mutex_unlock(header->objlock);
1249 /* ==================================
1250 * transCommitProcess
1252 * =================================
1255 void transCommitProcess(void ** oidwrlocked, int numoidwrlocked, int numoidwrtotal, void (*commitmethod)(void *, void *, void *), void * primitives, void * locals, void * params) {
1257 void transCommitProcess(void ** oidwrlocked, int numoidwrlocked) {
1259 objheader_t *header;
1262 struct objlist *ptr=newobjs;
1264 int max=ptr->offset;
1265 for(i=0; i<max; i++) {
1266 //clear the new flag
1267 ((struct ___Object___ *)ptr->objs[i])->___objstatus___=0;
1272 /* Copy from transaction cache -> main object store */
1273 for (i = numoidwrlocked-1; i >=0; i--) {
1274 /* Read from the main heap */
1275 header = (objheader_t *)oidwrlocked[i];
1277 GETSIZE(tmpsize, header);
1278 struct ___Object___ *dst=(struct ___Object___*)(((char *)oidwrlocked[i])+sizeof(objheader_t));
1279 struct ___Object___ *src=t_chashSearch(dst);
1280 dst->___cachedCode___=src->___cachedCode___;
1281 dst->___cachedHash___=src->___cachedHash___;
1282 A_memcpy(&dst[1], &src[1], tmpsize-sizeof(struct ___Object___));
1287 // call commit method
1290 branchstack.count=0;
1291 commitmethod(params, locals, primitives);
1294 /* Release write locks */
1296 for(i=numoidwrtotal-1; i>=0; i--) {
1298 for(i=numoidwrlocked-1; i>=0; i--) {
1300 header = (objheader_t *)oidwrlocked[i];
1302 write_unlock(&header->lock);
1306 /* clear trec and then release objects locked */
1309 int max=ptr->offset;
1310 for(i=max-1; i>=0; i--) {
1311 header = (objheader_t *)ptr->objs[i];
1312 header->trec = NULL;
1313 pthread_mutex_unlock(header->objlock);
1320 #if defined(STMSTATS)||defined(SOFTABORT)
1321 /** ========================================================================================
1322 * getTotalAbortCount (for traverseCache only)
1323 * params : start: start index of the loop
1324 * : stop: stop index of the loop
1325 * : startptr: pointer that points to where to start looking in the cache hash table
1326 * : numoidrdlocked : number of objects read that are locked
1327 * : oidrdlocked : array of objects read and currently locked
1328 * : oidrdversion : array of versions of object read
1329 * : oidrdage : array of ages of objects read ina transaction cache
1330 * : ObjSeqId : sequence Id/age to start the comparision with
1331 * =========================================================================================
1333 int getTotalAbortCount(int start, int stop, void *startptr, int numoidrdlocked,
1334 void *oidrdlocked, int *oidrdversion, int *oidrdage, int ObjSeqId, objheader_t *header, int *isObjTypeTraverse) {
1338 objheader_t *ObjToBeLocked=header;
1339 chashlistnode_t *curr = (chashlistnode_t *) startptr;
1340 chashlistnode_t *ptr = c_table;
1341 /* First go through all objects left in the cache that have not been covered yet */
1342 for(i = start; i < stop; i++) {
1345 /* Inner loop to traverse the linked list of the cache lookupTable */
1346 while(curr != NULL) {
1347 if(curr->key == NULL)
1349 objheader_t * headeraddr=&((objheader_t *) curr->val)[-1];
1350 objheader_t *header=(objheader_t *)(((char *)curr->key)-sizeof(objheader_t));
1351 unsigned int version = headeraddr->version;
1352 /* versions do not match */
1353 if(version != header->version) {
1355 header->abortCount++;
1356 if(ObjSeqId > headeraddr->accessCount) {
1357 ObjSeqId = headeraddr->accessCount;
1358 ObjToBeLocked = header;
1360 getTransSize(header, isObjTypeTraverse);
1369 /* Then go through all objects that are read and are currently present in the readLockedArray */
1370 if(numoidrdlocked>0) {
1371 for(i=0; i<numoidrdlocked; i++) {
1372 objheader_t *header = ((void **) oidrdlocked)[i];
1373 int OidAge = oidrdage[i];
1374 unsigned int version = oidrdversion[i];
1375 if(version != header->version) { /* versions do not match */
1377 header->abortCount++;
1378 if(ObjSeqId > OidAge) {
1380 ObjToBeLocked = header;
1382 getTransSize(header, isObjTypeTraverse);
1389 /* Acquire lock on the oldest object accessed in the transaction cache */
1390 if(ObjToBeLocked != NULL) {
1391 ABORTCOUNT(ObjToBeLocked);
1397 /** ========================================================================================
1398 * getTotalAbortCount2 (for alttraverseCache only)
1399 * params : startptr: pointer that points to where to start looking in the cache hash table
1400 * : numoidrdlocked : number of objects read that are locked
1401 * : oidrdlocked : array of objects read and currently locked
1402 * : oidrdversion : array of versions of object read
1403 * : oidrdage : array of ages of objects read ina transaction cache
1404 * : ObjSeqId : sequence Id/age to start the comparision with
1405 * =========================================================================================
1407 int getTotalAbortCount2(void *startptr, int numoidrdlocked, void *oidrdlocked,
1408 int *oidrdversion, int *oidrdage, int ObjSeqId, objheader_t *header, int *isObjTypeTraverse) {
1410 chashlistnode_t *curr = (chashlistnode_t *) startptr;
1411 objheader_t *ObjToBeLocked=header;
1413 /* Inner loop to traverse the linked list of the cache lookupTable */
1414 while(curr != NULL) {
1415 objheader_t *headeraddr=&((objheader_t *) curr->val)[-1];
1416 objheader_t *header=(objheader_t *)(((char *)curr->key)-sizeof(objheader_t));
1417 unsigned int version = headeraddr->version;
1418 /* versions do not match */
1419 if(version != header->version) {
1421 header->abortCount++;
1422 if(ObjSeqId > headeraddr->accessCount) {
1423 ObjSeqId = headeraddr->accessCount;
1424 ObjToBeLocked = header;
1426 getTransSize(header, isObjTypeTraverse);
1433 /* Then go through all objects that are read and are currently present in the readLockedArray */
1434 if(numoidrdlocked>0) {
1436 for(i=0; i<numoidrdlocked; i++) {
1437 objheader_t *header = ((void **)oidrdlocked)[i];
1438 unsigned int version = oidrdversion[i];
1439 int OidAge = oidrdage[i];
1440 if(version != header->version) { /* versions do not match */
1442 header->abortCount++;
1443 if(ObjSeqId > OidAge) {
1445 ObjToBeLocked = header;
1447 getTransSize(header, isObjTypeTraverse);
1454 if(ObjToBeLocked!=NULL) {
1455 ABORTCOUNT(ObjToBeLocked);
1462 * getReadAbortCount : Tells the number of aborts caused by objects that are read by
1463 * visiting the read array
1464 * params: int start, int stop are indexes to readLocked array
1465 * void *oidrdlocked = readLocked array
1466 * int *oidrdversion = version array
1467 * : oidrdage : array of ages of objects read ina transaction cache
1468 * : ObjSeqId : sequence Id/age to start the comparision with
1470 int getReadAbortCount(int start, int stop, void *oidrdlocked, int *oidrdversion,
1471 int *oidrdage, int ObjSeqId, objheader_t *header, int *isObjTypeTraverse) {
1474 objheader_t *ObjToBeLocked=header;
1476 /* Go through oids read that are locked */
1477 for(i = start; i < stop; i++) {
1478 objheader_t *header = ((void **)oidrdlocked)[i];
1479 unsigned int version = oidrdversion[i];
1480 int OidAge = oidrdage[i];
1481 if(version != header->version) { /* versions do not match */
1483 header->abortCount++;
1484 if(ObjSeqId > OidAge) {
1486 ObjToBeLocked = header;
1488 getTransSize(header, isObjTypeTraverse);
1494 if(ObjToBeLocked != NULL) {
1495 ABORTCOUNT(ObjToBeLocked);
1504 * params: Object header, ptr to garbage collector
1505 * Locks an object that causes aborts
1507 objheader_t * needLock(objheader_t *header, void *gl) {
1510 while((lockstatus = pthread_mutex_trylock(header->objlock))
1511 && ((ptr = header->trec) == NULL)) { //retry
1514 if(lockstatus==0) { //acquired lock
1516 header->trec = trec;
1517 } else { //failed to get lock
1521 //see if other thread is blocked
1522 if(ptr->blocked == 1) {
1523 //it might be block, so ignore lock and clear our blocked flag
1528 INTPTR ptrarray[]={1, (INTPTR)gl, (INTPTR) header};
1529 void *lockptr=header->objlock;
1530 stopforgc((struct garbagelist *)ptrarray);
1531 //grab lock and wait our turn
1532 pthread_mutex_lock(lockptr);
1534 header=(objheader_t *) ptrarray[2];
1536 pthread_mutex_lock(header->objptr);
1538 /* we have lock, so we are not blocked anymore */
1541 header->trec = trec;
1544 //trec->blocked is zero now
1546 /* Save the locked object */
1547 if (lockedobjs->offset<MAXOBJLIST) {
1548 lockedobjs->objs[lockedobjs->offset++]=header;
1550 struct objlist *tmp=malloc(sizeof(struct objlist));
1551 tmp->next=lockedobjs;
1552 tmp->objs[0]=header;
1560 * Inline fuction to get Transaction size per object type for those
1561 * objects that cause
1565 INLINE void getTransSize(objheader_t *header , int *isObjTypeTraverse) {
1566 (typesCausingAbort[TYPE(header)]).numabort++;
1567 if(isObjTypeTraverse[TYPE(header)] != 1)
1568 (typesCausingAbort[TYPE(header)]).numaccess+=c_numelements;
1569 isObjTypeTraverse[TYPE(header)]=1;