objective c - Core data getting locked with multi threading (_objectStoreLockCount > 1) -
i finding hard time in fixing core data db lock situation (_objectstorelockcount count > 1 when run in debug mode).
below architecture of application (mac osx):
- i using 2 nsoperationqueue perform activities. each queue having 20 activities running in parallel.
- once these activities done, each 1 of them put result data in nsoperationqueue has maximum concurrent operation count set 1.
- the result operation queue puts data in core data.
- there 2 other threads running. 1 of reading data core data. one, based on logic, updating existing records in core data.
- for core data handling, have created subclass of nsobject having 3 objects. each object has own nsmanagedobjectcontext - 1 reading data, 1 writing new records , 1 updating existing records. updating read managed object context whenever there change in write managed object context. 3 mocs have common persistent store co-ordinator.
- the application supposed run 24*7 above code bound repeat
issue: when run application randomly hangs or locks db. reaches state random times - 2 hours, 10 hours not defined...
i attaching core data handling part of application. please let me know if missing in core data handling:
@implementation myappdatarepository @synthesize persistentstorecoordinator = _persistentstorecoordinator; @synthesize managedobjectmodel = _managedobjectmodel; @synthesize writemanagedobjectcontext = _writemanagedobjectcontext; @synthesize readmanagedobjectcontext = _readmanagedobjectcontext; @synthesize updatemanagedobjectcontext = _updatemanagedobjectcontext; static myappdatarepository *sharedmyappwritedatarepository = nil; static myappdatarepository *sharedmyappreaddatarepository = nil; static myappdatarepository *sharedmyappupdatedatarepository = nil; + (myappdatarepository *)sharedmyappwritedatarepositorymyapp { static dispatch_once_t pred = 0; dispatch_once(&pred, ^{ sharedmyappwritedatarepository = [[self alloc] init]; [sharedmyappwritedatarepository persistentstorecoordinator]; [sharedmyappwritedatarepository writemanagedobjectcontext]; }); return sharedmyappwritedatarepository; } + (myappdatarepository *)sharedmyappreaddatarepositorymyapp { static dispatch_once_t pred = 0; dispatch_once(&pred, ^{ sharedmyappreaddatarepository = [[self alloc] init]; // or other init method [sharedmyappreaddatarepository persistentstorecoordinator]; [sharedmyappreaddatarepository readmanagedobjectcontext]; }); return sharedmyappreaddatarepository; } + (myappdatarepository *)sharedmyappupdatedatarepositorymyapp { static dispatch_once_t pred = 0; dispatch_once(&pred, ^{ sharedmyappupdatedatarepository = [[self alloc] init]; // or other init method [sharedmyappupdatedatarepository persistentstorecoordinator]; [sharedmyappupdatedatarepository updatemanagedobjectcontext]; }); return sharedmyappupdatedatarepository; } -(id)init { if ((self = [super init])) { if (!self.writemanagedobjectcontext) { myapplog(@"core data cannot initiated"); } } return self; } // returns directory application uses store core data store file. code uses directory named "com.apple.retail.myapp" in user's application support directory. - (nsurl *)applicationfilesdirectory { nsfilemanager *filemanager = [nsfilemanager defaultmanager]; nsurl *appsupporturl = [[filemanager urlsfordirectory:nsapplicationsupportdirectory indomains:nsuserdomainmask] lastobject]; return [appsupporturl urlbyappendingpathcomponent:@"com.apple.retail.myapp"]; } // creates if necessary , returns managed object model application. - (nsmanagedobjectmodel *)managedobjectmodel { if (_managedobjectmodel) { return _managedobjectmodel; } nsurl *modelurl = [[nsbundle mainbundle] urlforresource:@"myapp" withextension:@"momd"]; _managedobjectmodel = [[nsmanagedobjectmodel alloc] initwithcontentsofurl:modelurl]; return _managedobjectmodel; } // returns persistent store coordinator application. implementation creates , return coordinator, having added store application it. (the directory store created, if necessary.) - (nspersistentstorecoordinator *)persistentstorecoordinator { if (_persistentstorecoordinator) { return _persistentstorecoordinator; } nsmanagedobjectmodel *mom = [self managedobjectmodel]; if (!mom) { myapplog(@"%@:%@ no model generate store from", [self class], nsstringfromselector(_cmd)); return nil; } nsfilemanager *filemanager = [nsfilemanager defaultmanager]; nsurl *applicationfilesdirectory = [self applicationfilesdirectory]; nserror *error = nil; nsdictionary *properties = [applicationfilesdirectory resourcevaluesforkeys:@[nsurlisdirectorykey] error:&error]; if (!properties) { bool ok = no; if ([error code] == nsfilereadnosuchfileerror) { ok = [filemanager createdirectoryatpath:[applicationfilesdirectory path] withintermediatedirectories:yes attributes:nil error:&error]; } if (!ok) { [[nsapplication sharedapplication] presenterror:error]; return nil; } } else { if (![properties[nsurlisdirectorykey] boolvalue]) { // customize , localize error. nsstring *failuredescription = [nsstring stringwithformat:@"expected folder store application data, found file (%@).", [applicationfilesdirectory path]]; nsmutabledictionary *dict = [nsmutabledictionary dictionary]; [dict setvalue:failuredescription forkey:nslocalizeddescriptionkey]; error = [nserror errorwithdomain:@"your_error_domain" code:101 userinfo:dict]; [[nsapplication sharedapplication] presenterror:error]; return nil; } } //support automatic migration of core data nsmutabledictionary *optionsdictionary = [nsmutabledictionary dictionary]; [optionsdictionary setobject:[nsnumber numberwithbool:yes] forkey:nsmigratepersistentstoresautomaticallyoption]; [optionsdictionary setobject:[nsnumber numberwithbool:yes] forkey:nsinfermappingmodelautomaticallyoption]; nsurl *url = [applicationfilesdirectory urlbyappendingpathcomponent:@"myapp.sqlite"]; nspersistentstorecoordinator *coordinator = [[nspersistentstorecoordinator alloc] initwithmanagedobjectmodel:mom]; if (![coordinator addpersistentstorewithtype:nssqlitestoretype configuration:nil url:url options:optionsdictionary error:&error]) { [[nsapplication sharedapplication] presenterror:error]; return nil; } _persistentstorecoordinator = coordinator; return _persistentstorecoordinator; } // returns managed object context application (which bound persistent store coordinator application.) - (nsmanagedobjectcontext *)writemanagedobjectcontext { if (_writemanagedobjectcontext) { return _writemanagedobjectcontext; } nspersistentstorecoordinator *coordinator = [self persistentstorecoordinator]; if (!coordinator) { nsmutabledictionary *dict = [nsmutabledictionary dictionary]; [dict setvalue:@"failed initialize store" forkey:nslocalizeddescriptionkey]; [dict setvalue:@"there error building data file." forkey:nslocalizedfailurereasonerrorkey]; nserror *error = [nserror errorwithdomain:@"your_error_domain" code:9999 userinfo:dict]; [[nsapplication sharedapplication] presenterror:error]; return nil; } _writemanagedobjectcontext = [[nsmanagedobjectcontext alloc] init]; [_writemanagedobjectcontext setpersistentstorecoordinator:coordinator]; return _writemanagedobjectcontext; } - (nsmanagedobjectcontext *)readmanagedobjectcontext { if (_readmanagedobjectcontext) { return _readmanagedobjectcontext; } nspersistentstorecoordinator *coordinator = [self persistentstorecoordinator]; if (!coordinator) { nsmutabledictionary *dict = [nsmutabledictionary dictionary]; [dict setvalue:@"failed initialize store" forkey:nslocalizeddescriptionkey]; [dict setvalue:@"there error building data file." forkey:nslocalizedfailurereasonerrorkey]; nserror *error = [nserror errorwithdomain:@"your_error_domain" code:9999 userinfo:dict]; [[nsapplication sharedapplication] presenterror:error]; return nil; } _readmanagedobjectcontext = [[nsmanagedobjectcontext alloc] init]; [_readmanagedobjectcontext setpersistentstorecoordinator:coordinator]; return _readmanagedobjectcontext; } - (nsmanagedobjectcontext *)updatemanagedobjectcontext { if (_updatemanagedobjectcontext) { return _updatemanagedobjectcontext; } nspersistentstorecoordinator *coordinator = [self persistentstorecoordinator]; if (!coordinator) { nsmutabledictionary *dict = [nsmutabledictionary dictionary]; [dict setvalue:@"failed initialize store" forkey:nslocalizeddescriptionkey]; [dict setvalue:@"there error building data file." forkey:nslocalizedfailurereasonerrorkey]; nserror *error = [nserror errorwithdomain:@"your_error_domain" code:9999 userinfo:dict]; [[nsapplication sharedapplication] presenterror:error]; return nil; } _updatemanagedobjectcontext = [[nsmanagedobjectcontext alloc] init]; [_updatemanagedobjectcontext setpersistentstorecoordinator:coordinator]; return _updatemanagedobjectcontext; } // returns nsundomanager application. in case, manager returned of managed object context application. - (nsundomanager *)windowwillreturnundomanager:(nswindow *)window { return [[self writemanagedobjectcontext] undomanager]; } // performs save action application, send save: message application's managed object context. encountered errors presented user. - (ibaction)saveaction:(id)sender { nserror *error = nil; if (![[self writemanagedobjectcontext] commitediting]) { myapplog(@"%@:%@ unable commit editing before saving", [self class], nsstringfromselector(_cmd)); } if (![[self writemanagedobjectcontext] save:&error]) { [[nsapplication sharedapplication] presenterror:error]; } } - (bool)savedataindatabase:(nsmanagedobjectcontext *)icontext { bool datasavedsuccessfully = yes; nserror *error = nil; if (![icontext commitediting]) { myapplog(@"%@:%@ unable commit editing before saving", [self class], nsstringfromselector(_cmd)); } [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(handledidsavenotification:) name:nsmanagedobjectcontextdidsavenotification object:icontext]; if (![icontext save:&error]) { datasavedsuccessfully = no; [[nsapplication sharedapplication] presenterror:error]; } // unregister notification [[nsnotificationcenter defaultcenter] removeobserver:self name:nsmanagedobjectcontextdidsavenotification object:icontext]; return datasavedsuccessfully; } // merge changes write managed object context read managed object context - (void)handledidsavenotification:(nsnotification *)inotification { [[myappdatarepository sharedmyappreaddatarepository].readmanagedobjectcontext mergechangesfromcontextdidsavenotification:inotification]; } - (nsapplicationterminatereply)applicationshouldterminate:(nsapplication *)sender { // save changes in application's managed object context before application terminates. if (!_writemanagedobjectcontext) { return nsterminatenow; } if (![[self writemanagedobjectcontext] commitediting]) { myapplog(@"%@:%@ unable commit editing terminate", [self class], nsstringfromselector(_cmd)); return nsterminatecancel; } if (![[self writemanagedobjectcontext] haschanges]) { return nsterminatenow; } nserror *error = nil; if (![[self writemanagedobjectcontext] save:&error]) { // customize code block include application-specific recovery steps. bool result = [sender presenterror:error]; if (result) { return nsterminatecancel; } nsstring *question = nslocalizedstring(@"could not save changes while quitting. quit anyway?", @"quit without saves error question message"); nsstring *info = nslocalizedstring(@"quitting lose changes have made since last successful save", @"quit without saves error question info"); nsstring *quitbutton = nslocalizedstring(@"quit anyway", @"quit anyway button title"); nsstring *cancelbutton = nslocalizedstring(@"cancel", @"cancel button title"); nsalert *alert = [[nsalert alloc] init]; [alert setmessagetext:question]; [alert setinformativetext:info]; [alert addbuttonwithtitle:quitbutton]; [alert addbuttonwithtitle:cancelbutton]; nsinteger answer = [alert runmodal]; if (answer == nsalertalternatereturn) { return nsterminatecancel; } } return nsterminatenow; } - (void)inserttestresults:(nsarray *)itestdata withupdateflag:(int)iupdateflag withdiscarddlag:(int)idiscardflag { nsmanagedobjectcontext *awritemanagedobjcontext = [[myappdatarepository sharedmyappwritedatarepositorymyapp] writemanagedobjectcontext]; nsmanagedobject *atestresults; nsmutablearray *atestidarray=[nsmutablearray array]; (myapptestresultsmodel *itestresultsdata in itestdata) { nsstring *atesttype=[itestresultsdata valueforkey:kmyapptesttype]; if ([atesttype isequaltostring:kmyapptest1type]) { atestresults=[nsentitydescription insertnewobjectforentityforname:kmyapptest1resultsentity inmanagedobjectcontext:awritemanagedobjcontext]; } else if ([atesttype isequaltostring:kmyapptest2type]) { atestresults=[nsentitydescription insertnewobjectforentityforname:kmyapptest2resultsentity inmanagedobjectcontext:awritemanagedobjcontext]; } else if ([atesttype isequaltostring:kmyapptest3type]) { atestresults=[nsentitydescription insertnewobjectforentityforname:kmyapptest3resultsentity inmanagedobjectcontext:awritemanagedobjcontext]; } else if ([atesttype isequaltostring:kmyapptest4type]) { atestresults=[nsentitydescription insertnewobjectforentityforname:kmyapptest4resultsentity inmanagedobjectcontext:awritemanagedobjcontext]; } nserror *anerror=nil; if (![self savedataindatabase:awritemanagedobjcontext]) { myapplog(@"cannot save!: %@", [anerror localizeddescription]); } else { // post saved message main window nsstring *alog = [nsstring stringwithformat:@"\n \n%@ test results saved \n %@",[itestresultsdata valueforkey:kmyapptesttype], atestresults]; myapplog(@"%@",alog); myappinfolog(@"saved test results (in client db) test id = %@ test type = %@ app id = %@ network = %@ status = %@", [itestresultsdata valueforkey:kmyapptestid], [itestresultsdata valueforkey:kmyapptesttype], [itestresultsdata valueforkey:kmyappappidattribute], [itestresultsdata valueforkey:kmyapptestnetwork], [itestresultsdata valueforkey:kmyappteststatusattribute]); [atestidarray addobject:[atestresults valueforkey:kmyapptestidattribute]]; // update isreadytoflag if (iupdateflag == 1) [myappfetchtestconfigdetails updatereadytorunfortestid:[nsarray arraywitharray:atestidarray] withint:iupdateflag]; } } }
below operation queue implementation thread 2 (that has been filled multiple threads):
@implementation myappsavetestresultcontroller - (id)init { if ((self = [super init]) != nil) { self.queue = [[nsoperationqueue alloc] init]; [self.queue setmaxconcurrentoperationcount:1]; } return self; } + (myappsavetestresultcontroller *)sharedsaveresultscontroller { static dispatch_once_t pred = 0; dispatch_once(&pred, ^{ sharedsaveresultscontroller = [[self alloc] init]; // or other init method }); return sharedsaveresultscontroller; } - (void)startsaveoperation { myappsavetestresultsoperation *asaveoperation = [[myappsavetestresultsoperation alloc] initwithtestresults:self.testresults updateflag:self.updateflag anddiscarddlag:self.discardflag]; [self.queue addoperation:asaveoperation]; } @implementation mysavetestresultsoperation - (id)initwithtestresults:(nsarray *)itestdata updateflag:(int)iupdateflag anddiscarddlag:(int)idiscardflag { if ((self = [super init]) != nil) { self.testresults = itestdata; self.updateflag = iupdateflag; self.discardflag = idiscardflag; } return self; } - (void)start { if (![nsthread ismainthread]) { [self performselectoronmainthread:@selector(start) withobject:nil waituntildone:no]; return; } // check cancellation if ([self iscancelled]) { [self completeoperation]; return; } // executing [self willchangevalueforkey:@"isexecuting"]; executing = yes; [self didchangevalueforkey:@"isexecuting"]; // begin [self beginoperation]; } - (void)beginoperation { @try { [[myappdatarepository sharedmyappwritedatarepository] inserttestresults:results withupdateflag:self.updateflag withdiscarddlag:self.discardflag]; [self completeoperation]; } @catch(nsexception * e) { } }
below piece of code called activity threads (as many 20 concurrent threads) fill in data myappsavetestresultcontroller:
myappsavetestresultcontroller *asavetestresultcontroller = [myappsavetestresultcontroller sharedsaveresultscontroller]; [asavetestresultcontroller savetestresults:[nsarray arraywithobject:data] updateflag:[[nsnumber numberwithbool:[ablockself checkpendingtestsfortestid:anid]] intvalue] discardflag:0]; [asavetestresultcontroller startsaveoperation];
Comments
Post a Comment