void initGlobals() { // LEAVE THIS STATEMENT pthread_mutex_init(&outMutex, NULL); // TODO: Complete this function maleCount = 0; femaleCount = 0; sem_init(&male, 0, 1); sem_init(&female, 0, 1); pthread_mutex_init(&limitMaleMutex, NULL); pthread_mutex_init(&limitFemaleMutex, NULL); pthread_mutex_init(&maleCountMutex, NULL); pthread_mutex_init(&femaleCountMutex, NULL); // TODO: Complete for scheduler addLock("limitMaleMutex", &limitMaleMutex); addLock("limitFemaleMutex", &limitFemaleMutex); addLock("maleCountMutex", &maleCountMutex); addLock("femaleCountMutex", &femaleCountMutex); addSemaphore("female", SEM_FEMALE_INITIAL, &female); addSemaphore("male", SEM_MALE_INITIAL, &male); }
/// \brief Check a function's CFG for thread-safety violations. /// /// We traverse the blocks in the CFG, compute the set of mutexes that are held /// at the end of each block, and issue warnings for thread safety violations. /// Each block in the CFG is traversed exactly once. void runThreadSafetyAnalysis(AnalysisContext &AC, ThreadSafetyHandler &Handler) { CFG *CFGraph = AC.getCFG(); if (!CFGraph) return; const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl()); if (!D) return; // Ignore anonymous functions for now. if (D->getAttr<NoThreadSafetyAnalysisAttr>()) return; Lockset::Factory LocksetFactory; // FIXME: Swith to SmallVector? Otherwise improve performance impact? std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(), LocksetFactory.getEmptyMap()); std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(), LocksetFactory.getEmptyMap()); // We need to explore the CFG via a "topological" ordering. // That way, we will be guaranteed to have information about required // predecessor locksets when exploring a new block. TopologicallySortedCFG SortedGraph(CFGraph); CFGBlockSet VisitedBlocks(CFGraph); if (!SortedGraph.empty() && D->hasAttrs()) { const CFGBlock *FirstBlock = *SortedGraph.begin(); Lockset &InitialLockset = EntryLocksets[FirstBlock->getBlockID()]; const AttrVec &ArgAttrs = D->getAttrs(); for(unsigned i = 0; i < ArgAttrs.size(); ++i) { Attr *Attr = ArgAttrs[i]; SourceLocation AttrLoc = Attr->getLocation(); if (SharedLocksRequiredAttr *SLRAttr = dyn_cast<SharedLocksRequiredAttr>(Attr)) { for (SharedLocksRequiredAttr::args_iterator SLRIter = SLRAttr->args_begin(), SLREnd = SLRAttr->args_end(); SLRIter != SLREnd; ++SLRIter) InitialLockset = addLock(Handler, LocksetFactory, InitialLockset, *SLRIter, D, LK_Shared, AttrLoc); } else if (ExclusiveLocksRequiredAttr *ELRAttr = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) { for (ExclusiveLocksRequiredAttr::args_iterator ELRIter = ELRAttr->args_begin(), ELREnd = ELRAttr->args_end(); ELRIter != ELREnd; ++ELRIter) InitialLockset = addLock(Handler, LocksetFactory, InitialLockset, *ELRIter, D, LK_Exclusive, AttrLoc); } } } for (TopologicallySortedCFG::iterator I = SortedGraph.begin(), E = SortedGraph.end(); I!= E; ++I) { const CFGBlock *CurrBlock = *I; int CurrBlockID = CurrBlock->getBlockID(); VisitedBlocks.insert(CurrBlock); // Use the default initial lockset in case there are no predecessors. Lockset &Entryset = EntryLocksets[CurrBlockID]; Lockset &Exitset = ExitLocksets[CurrBlockID]; // Iterate through the predecessor blocks and warn if the lockset for all // predecessors is not the same. We take the entry lockset of the current // block to be the intersection of all previous locksets. // FIXME: By keeping the intersection, we may output more errors in future // for a lock which is not in the intersection, but was in the union. We // may want to also keep the union in future. As an example, let's say // the intersection contains Mutex L, and the union contains L and M. // Later we unlock M. At this point, we would output an error because we // never locked M; although the real error is probably that we forgot to // lock M on all code paths. Conversely, let's say that later we lock M. // In this case, we should compare against the intersection instead of the // union because the real error is probably that we forgot to unlock M on // all code paths. bool LocksetInitialized = false; for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(), PE = CurrBlock->pred_end(); PI != PE; ++PI) { // if *PI -> CurrBlock is a back edge if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) continue; int PrevBlockID = (*PI)->getBlockID(); if (!LocksetInitialized) { Entryset = ExitLocksets[PrevBlockID]; LocksetInitialized = true; } else { Entryset = intersectAndWarn(Handler, Entryset, ExitLocksets[PrevBlockID], LocksetFactory, LEK_LockedSomePredecessors); } } BuildLockset LocksetBuilder(Handler, Entryset, LocksetFactory); for (CFGBlock::const_iterator BI = CurrBlock->begin(), BE = CurrBlock->end(); BI != BE; ++BI) { if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI)) LocksetBuilder.Visit(const_cast<Stmt*>(CfgStmt->getStmt())); } Exitset = LocksetBuilder.getLockset(); // For every back edge from CurrBlock (the end of the loop) to another block // (FirstLoopBlock) we need to check that the Lockset of Block is equal to // the one held at the beginning of FirstLoopBlock. We can look up the // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map. for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), SE = CurrBlock->succ_end(); SI != SE; ++SI) { // if CurrBlock -> *SI is *not* a back edge if (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) continue; CFGBlock *FirstLoopBlock = *SI; Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()]; Lockset LoopEnd = ExitLocksets[CurrBlockID]; intersectAndWarn(Handler, LoopEnd, PreLoop, LocksetFactory, LEK_LockedSomeLoopIterations); } } Lockset InitialLockset = EntryLocksets[CFGraph->getEntry().getBlockID()]; Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()]; // FIXME: Should we call this function for all blocks which exit the function? intersectAndWarn(Handler, InitialLockset, FinalLockset, LocksetFactory, LEK_LockedAtEndOfFunction); }
str OPToltpImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, limit, slimit, updates=0; InstrPtr p, q, lcks; int actions = 0; InstrPtr *old; lng usec = GDKusec(); OLTPlocks wlocks, rlocks; char buf[256]; str msg = MAL_SUCCEED; (void) pci; (void) cntxt; (void) stk; /* to fool compilers */ old= mb->stmt; limit= mb->stop; slimit = mb->ssize; // We use a fake collection of objects to speed up the checking later. OLTPclear(wlocks); OLTPclear(rlocks); for (i = 0; i < limit; i++) { p = old[i]; if( getModuleId(p) == sqlRef && getFunctionId(p) == bindRef) addLock(cntxt,rlocks, mb, p, p->retc + 1, p->retc + 2); else if( getModuleId(p) == sqlRef && getFunctionId(p) == bindidxRef) addLock(cntxt,rlocks, mb, p, p->retc + 1, p->retc + 2); else if( getModuleId(p) == sqlRef && getFunctionId(p) == appendRef ){ addLock(cntxt,wlocks, mb, p, p->retc + 1, p->retc + 2); updates++; } else if( getModuleId(p) == sqlRef && getFunctionId(p) == updateRef ){ addLock(cntxt,wlocks, mb, p, p->retc + 1, p->retc + 2); updates++; } else if( getModuleId(p) == sqlRef && getFunctionId(p) == deleteRef ){ addLock(cntxt,wlocks, mb, p, p->retc + 1, p->retc + 2); updates++; } else if( getModuleId(p) == sqlcatalogRef ){ addLock(cntxt,wlocks, mb, p, 0,0); updates++; } } if( updates == 0) return 0; // Get a free instruction, don't get it from mb lcks= newInstruction(0, oltpRef,lockRef); getArg(lcks,0)= newTmpVariable(mb, TYPE_void); for( i = 0; i< MAXOLTPLOCKS; i++) if( wlocks[i]) lcks = pushInt(mb, lcks, i); else if( rlocks[i]) lcks = pushInt(mb, lcks, -i); if( lcks->argc == 1 ){ freeInstruction(lcks); return MAL_SUCCEED; } // Now optimize the code if ( newMalBlkStmt(mb,mb->ssize + 6) < 0) { freeInstruction(lcks); return 0; } pushInstruction(mb,old[0]); pushInstruction(mb,lcks); for (i = 1; i < limit; i++) { p = old[i]; if( p->token == ENDsymbol){ // unlock all if there is an error q= newCatchStmt(mb,"MALexception"); q= newExitStmt(mb,"MALexception"); q= newCatchStmt(mb,"SQLexception"); q= newExitStmt(mb,"SQLexception"); q= copyInstruction(lcks); if( q == NULL){ for(; i<slimit; i++) if( old[i]) freeInstruction(old[i]); GDKfree(old); throw(MAL,"optimizer.oltp", SQLSTATE(HY001) MAL_MALLOC_FAIL); } setFunctionId(q, releaseRef); pushInstruction(mb,q); } pushInstruction(mb,p); } for(; i<slimit; i++) if( old[i]) freeInstruction(old[i]); GDKfree(old); /* Defense line against incorrect plans */ chkTypes(cntxt->usermodule, mb, FALSE); //chkFlow(mb); //chkDeclarations(mb); /* keep all actions taken as a post block comment */ usec = GDKusec()- usec; snprintf(buf,256,"%-20s actions=%2d time=" LLFMT " usec","oltp",actions, usec); newComment(mb,buf); if( actions >= 0) addtoMalBlkHistory(mb); return msg; }