Friday, May 20, 2016

Pick:Set's and their Manager

Well, I did it. Just like that, I committed weeks of work to make Pick::Set's (more) MT-safe, and unify the I/O handling of them. What I did wouldn't have been possible without Kris' work with the new RefMan<T> object handling. Here goes.



Monitorable


First of all, Pick::Set's are now Monitorable. Such an object promises to be MT-compliant, and notify changes in its state. That means, for example, multi-read locking. So no public data members or even references to internal, everything is done via get and set functions. For 'loose' Set's, unstored just for work, everything stays the same - albeit that you can no longer access internals directly. Oh and they have to be created like:
RefMan<Pick::Set> ps = new Pick::Set;
Because of the ref counting there is no longer a destructor. Small cost for big upside; and you'll get used to it.


The SetMGR


Then for everything stored, there is a new Pick::SetMGR() object. You ask it for a set with a set ID (the IOObj's MultiID), and it will make sure it's loaded. Like in:
uiString errmsg; 
ConstRefMan<Pick::Set> ps = Pick::SetMGR().fetch( id, errmsg ); 
if ( !errmsg.isEmpty() ) 
    { errmsg_ = errmsg; return false; }
The Set you are looking for may already be in memory, but it may also be read from disk. All you know is you get a ref, and other objects may also be looking at the same set. Or even, they may be changing it.


Monitoring a Set


If I want to know about some change, then:
mAttachCB( ps->objectChanged(), MyObj::pickSetChgCB );
By unpacking the 'Capsule' in the callback you can figure out what actually happened - but that's the topic of some other post. The thing is, the Pick::SetMGR() is not the keeper of events, like the old Pick::Mgr() was. Consequently, the new SetMGR is all about storing and sharing. Almost like a real OO database.
For example, all Set's, being derived from Monitorable, have a dirtyCount(). The SetMGR keeps special objects called Pick::SetSaver that keep track of the last saved dirty count so they know whether a Set was changed since it was added, read, or last saved.
If your stuff needs to be MT-ready, you'll want some kind of lock to prevent others from changing the Set while you are iterating over it. That is there, it's called a MonitorLock. Typically, one would do:
MonitorLock ml( *ps ); 
for ( int idx=0; idxsize(); idx++ ) 
{ 
    const Coord pos = ps->getPos( idx ); 
    // etc. 
} 
ml.unlockNow();
One thing: do not try to change the set while iterating. That is, unless you want to study deadlocks. Editing a set while having a MonitorLock on is simply not possible. Collect the changes may be an (MT-unsafe) idea. Or make a copy, and edit that. Then to finish copy back:
RefMan<Pick::Set> editedset = new Pick::Set( *ps ); 
doChanges( *editedset ); 
*ps = *editedset;


New Sets


To add a set, you could do something like:
RefMan<Pick::Set> ps = new Pick::Set;
fillPS( *ps );
uiString errmsg = Pick::SetMGR().store( *ps, id );
if ( !errmsg.isEmpty() )
    { errmsg_ = errmsg; return false; }
So, how do you get an ID? Well, quite possibly from the uiPickSetIOObjSel in uipicksettools.h. If you want no user interaction, you can still just give the Set a good name and store it with that:
uiString errmsg = Pick::SetMGR().store( *ps );
That will work, but you may just overwrite something, maybe something precious that another object is working with. Try:
if ( Pick::SetMGR().nameExists(ps->name()) )
{
    // Hmmm ... what now?
}


What's next?


On my TODO list is now to bring back undo handling (better than it was) and, the experienced MVC people probably guessed, burst event handling. My plan is to do that via a new object called ChangeNotifyBlocker combined with 'Entire object changed' change data.
And then ... we may have a template for other objects, to make them MT-ready, sharable, monitorable, tranparently stored. Wavelets seem a (rather simple) candidate, much more difficult but probably very rewarding may be wells.