Note that there are some explanatory texts on larger screens.

plurals
  1. PODesign decision: should I provide a thread-safety version for all low-level library?
    primarykey
    data
    text
    <p>I've experience a really painful java learning curve and still a newbie.</p> <p>I'm designing a low-level library class, in the future it possibly can be used in an multi-threading environment or just in a single thread. I have no idea of that.</p> <p><strong>I can let user of this class to synchronize from outside. But that will be much more inefficient than provide a thread-safety version.</strong></p> <p>This is p-code.</p> <pre><code>Class Example{ public int checkAndProcess(){ WriteLock writeLock=this.getWriteLock(); ReadLock readLock=new ReadLock(writeLock); int a; try{ lockManager.lock(readLock); a=readSomething(); }finally{ lockManager.release(readLock); } if(a!=null){ return a; } try{ lockManager.lock(writeLock); a=doSomeProcessing(); }finally{ lockManager.release(writeLock); } return a; } } </code></pre> <p>It will be much faster than synchronize from outside because readlock doesn't block. It's create and garbage-collected in every method call.</p> <p>Problem:</p> <p><strong>Overhead</strong>. WriteLock is pretty complex, ReadLock is cheap and simple but it's created in every method call(and possibly multiple), so still an overhead. </p> <p>Should I provide a thread-safety version of every such class? Every open source library doesn't do that. But if I don't provide it, let user synchronize from outside, performance will be lowered.</p> <p>Or is there any better way ?</p> <p>Edit:</p> <p>Should I split it?</p> <p>Split it into a stateless processor and a store, and let user to create readLock/writeLock to lock? If I do that, the store will be totally designed for the processor, doesn't have much meaning for other class, and the library will be quickly boomed by those things.</p> <p>This is my real code. You can ignore it if you don't like. </p> <pre><code>package lazycatTools.runtime; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.jobs.Job; import org.osgi.framework.AllServiceListener; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; public class DynamicServiceTracker { private final HashMap&lt;Long,Object&gt; _serviceCache; private final HashMap&lt;String,Long&gt; _keyCache; private final MultiResourceSchedulingRule _writeLock; private final ServiceListener _tracker; private final BundleContext _context; public DynamicServiceTracker(BundleContext context){ Assert.isLegal(context!=null); _serviceCache=new HashMap&lt;Long,Object&gt;(); _keyCache=new HashMap&lt;String,Long&gt;(); HashSet&lt;Object&gt; lockResource=new HashSet&lt;Object&gt;(4); lockResource.add(_serviceCache); lockResource.add(_keyCache); _writeLock=new MultiResourceSchedulingRule&lt;DynamicServiceTracker,Object&gt;(this,lockResource); _context=context; _tracker=new AllServiceListener(){ @Override public void serviceChanged(ServiceEvent event) { if(event.getType()==ServiceEvent.UNREGISTERING){ ServiceReference&lt;?&gt; ref=event.getServiceReference(); Long sid=(Long)ref.getProperty(Constants.SERVICE_ID); String[] classes=(String[])ref.getProperty(Constants.OBJECTCLASS); boolean ungetService=false; try{ Job.getJobManager().beginRule(_writeLock, null); for(String clazz : classes){ if(_keyCache.get(clazz)==sid){ _keyCache.remove(clazz); break; } } if(_serviceCache.containsKey(sid)){ _serviceCache.remove(sid); ungetService=true; } }finally{ Job.getJobManager().endRule(_writeLock); } if(ungetService){ //The order of ungetting a serviceReference is not important _context.ungetService(ref); } SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock); try{ Job.getJobManager().beginRule(readLock, null); if(_serviceCache.size()==0){ _context.removeServiceListener(_tracker); } }finally{ Job.getJobManager().endRule(readLock); } } } }; } public Object getService(String clazz) throws Exception{ Object cachedService=null; Long key; SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock); try{ Job.getJobManager().beginRule(readLock, null); key=_keyCache.get(clazz); if(key!=null){ cachedService=_serviceCache.get(key); } }finally{ Job.getJobManager().endRule(readLock); } if(cachedService!=null){ return cachedService; } ServiceReference&lt;?&gt; ref=_context.getServiceReference(clazz); Long sid=(Long)ref.getProperty(Constants.SERVICE_ID); Object newService=_context.getService(ref); try{ Job.getJobManager().beginRule(_writeLock, null); key=_keyCache.get(clazz); if(key!=null){ cachedService=_serviceCache.get(key); }else{ _keyCache.put(clazz,sid); _serviceCache.put(sid, newService); } }finally{ Job.getJobManager().endRule(_writeLock); } if(cachedService!=null){ _context.ungetService(ref); return cachedService; }else{ _context.addServiceListener(_tracker); return newService; } } public &lt;Type&gt; Type getService(Class&lt;Type&gt; clazz){ Object cachedService=null; Long key; SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock); try{ Job.getJobManager().beginRule(readLock, null); key=_keyCache.get(clazz); if(key!=null){ cachedService=_serviceCache.get(key); } }finally{ Job.getJobManager().endRule(readLock); } if(cachedService!=null){ @SuppressWarnings("unchecked") Type castedService=(Type)cachedService; return castedService; } ServiceReference&lt;Type&gt; ref=_context.getServiceReference(clazz); Long sid=(Long)ref.getProperty(Constants.SERVICE_ID); Type newService=_context.getService(ref); try{ Job.getJobManager().beginRule(_writeLock, null); key=_keyCache.get(clazz); if(key!=null){ cachedService=_serviceCache.get(key); }else{ _keyCache.put(clazz.getName(),sid); _serviceCache.put(sid, newService); } }finally{ Job.getJobManager().endRule(_writeLock); } if(cachedService!=null){ _context.ungetService(ref); @SuppressWarnings("unchecked") Type castedService=(Type)cachedService; return castedService; }else{ _context.addServiceListener(_tracker); return newService; } } public Object[] getServices(String clazz,String filter) throws InvalidSyntaxException{ ServiceReference&lt;?&gt;[] refs=_context.getServiceReferences(clazz,filter); if(refs==null){ return null; } Object[] services=new Object[refs.length]; int count=refs.length; boolean[] serviceAbsence=new boolean[refs.length]; Long[] SIDs=new Long[refs.length]; for(int i=0;i&lt;=count-1;i++){ ServiceReference&lt;?&gt; ref=refs[i]; SIDs[i]=(Long)ref.getProperty(Constants.SERVICE_ID); } boolean loop=true; SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock); while(loop){ try{ Job.getJobManager().beginRule(readLock, null); for(int i=0;i&lt;=count-1;i++){ if(_serviceCache.containsKey(SIDs[i])==false){ serviceAbsence[i]=true; } } }finally{ Job.getJobManager().endRule(readLock); } for(int i=0;i&lt;=count-1;i++){ if(serviceAbsence[i]==true){ services[i]=_context.getService(refs[i]); } } try{ Job.getJobManager().beginRule(_writeLock, null); boolean gotNewRequire=false; for(int i=0;i&lt;=count-1;i++){ if(_serviceCache.containsKey(SIDs[i])==false &amp;&amp; services[i]==null){ serviceAbsence[i]=true; gotNewRequire=true; } } if(gotNewRequire==false){ for(int i=0;i&lt;=count-1;i++){ Object service=services[i]; if(service!=null){ _serviceCache.put(SIDs[i], service); }else{ services[i]=_serviceCache.get(SIDs[i]); } } loop=false; } }finally{ Job.getJobManager().endRule(_writeLock); } } _context.addServiceListener(_tracker); return services; } public &lt;Type&gt; Collection&lt;Type&gt; getServices(Class&lt;Type&gt; clazz,String filter) throws InvalidSyntaxException{ Collection&lt;ServiceReference&lt;Type&gt;&gt; refsCollection=_context.getServiceReferences(clazz,filter); HashMap&lt;Integer,Type&gt; services=new HashMap&lt;Integer,Type&gt;(refsCollection.size()+1,1.0f); if(refsCollection.size()==0){ return services.values(); } ArrayList&lt;ServiceReference&lt;Type&gt;&gt; refs=new ArrayList&lt;ServiceReference&lt;Type&gt;&gt;(refsCollection); int count=refs.size(); boolean[] serviceAbsence=new boolean[refs.size()]; Long[] SIDs=new Long[refs.size()]; for(int i=0;i&lt;=count-1;i++){ ServiceReference&lt;Type&gt; ref=refs.get(i); SIDs[i]=(Long)ref.getProperty(Constants.SERVICE_ID); } boolean loop=true; SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock); while(loop){ try{ Job.getJobManager().beginRule(readLock, null); for(int i=0;i&lt;=count-1;i++){ if(_serviceCache.containsKey(SIDs[i])==false){ serviceAbsence[i]=true; } } }finally{ Job.getJobManager().endRule(readLock); } for(int i=0;i&lt;=count-1;i++){ if(serviceAbsence[i]==true){ services.put(i, _context.getService(refs.get(i))); } } try{ Job.getJobManager().beginRule(_writeLock, null); boolean gotNewRequire=false; for(int i=0;i&lt;=count-1;i++){ if(_serviceCache.containsKey(SIDs[i])==false &amp;&amp; services.containsKey(i)==false){ serviceAbsence[i]=true; gotNewRequire=true; } } if(gotNewRequire==false){ for(int i=0;i&lt;=count-1;i++){ Object service=services.get(i); if(service!=null){ _serviceCache.put(SIDs[i], service); }else{ @SuppressWarnings("unchecked") Type cachedService=(Type)_serviceCache.get(SIDs[i]); services.put(i,cachedService); } } loop=false; } }finally{ Job.getJobManager().endRule(_writeLock); } } _context.addServiceListener(_tracker); return services.values(); } } </code></pre> <p>This is MultiResourceSchedulingRule.</p> <pre><code>package lazycatTools.runtime; import java.util.Collections; import java.util.Set; import java.util.HashSet; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.jobs.ISchedulingRule; public class MultiResourceSchedulingRule&lt;ParentType,ResourceType&gt; extends ResourceBindingSchedulingRule&lt;ParentType&gt; implements IMultiResourceSchedulingRule&lt;ParentType,ResourceType&gt; { private final Set&lt;ResourceType&gt; _resources; public MultiResourceSchedulingRule(ParentType parent){ this(parent,new HashSet&lt;ResourceType&gt;()); } public MultiResourceSchedulingRule(ParentType parent,Set&lt;ResourceType&gt; resources){ super(parent); Assert.isLegal(resources!=null); _resources=resources; } @Override public boolean isConflicting(ISchedulingRule rule){ if(rule==this){ return true; } if(rule instanceof IResourceBindingSchedulingRule&lt;?&gt;){ final IResourceBindingSchedulingRule&lt;?&gt; casted=(IResourceBindingSchedulingRule&lt;?&gt;)rule; if(_resources.contains(casted.getResource())){ return true; } } if(rule instanceof IMultiResourceSchedulingRule&lt;?,?&gt;){ final IMultiResourceSchedulingRule&lt;?,?&gt; casted=(IMultiResourceSchedulingRule&lt;?,?&gt;)rule; if(Collections.disjoint(_resources,casted.getResources())==false){ return true; } } return false; } @Override public boolean contains(ISchedulingRule rule){ if(rule==this){ return true; } if(rule instanceof IResourceBindingSchedulingRule&lt;?&gt;){ final IResourceBindingSchedulingRule&lt;?&gt; casted=(IResourceBindingSchedulingRule&lt;?&gt;)rule; if(_resources.contains(casted.getResource())){ return true; } } if(rule instanceof IMultiResourceSchedulingRule){ final IMultiResourceSchedulingRule&lt;?,?&gt; casted=(IMultiResourceSchedulingRule&lt;?,?&gt;)rule; if(_resources.containsAll(casted.getResources())){ return true; } } return false; } @Override public Set&lt;ResourceType&gt; getResources() { return Collections.&lt;ResourceType&gt;unmodifiableSet(_resources); } } </code></pre> <p>And ResourceBindingSchedulingRule.</p> <pre><code>package lazycatTools.runtime; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.jobs.ISchedulingRule; public class ResourceBindingSchedulingRule&lt;ResourceType&gt; implements IResourceBindingSchedulingRule&lt;ResourceType&gt; { private final ResourceType _resource; public ResourceBindingSchedulingRule(ResourceType resource){ Assert.isLegal(resource!=null); _resource=resource; } /* (non-Javadoc) * @see lazycatTools.runtime.IResourceBindingSchedulingRule#getResource() */ @Override public ResourceType getResource() { return _resource; } @Override public boolean contains(ISchedulingRule rule) { return isConflicting(rule); } @Override public boolean isConflicting(ISchedulingRule rule) { if(rule==this){ return true; } if(rule instanceof IResourceBindingSchedulingRule&lt;?&gt;){ final IResourceBindingSchedulingRule&lt;?&gt; casted=(IResourceBindingSchedulingRule&lt;?&gt;)rule; return _resource==casted.getResource(); } return false; } } </code></pre> <p>SharedSchedulingRule which is used as the readLock in the example.</p> <pre><code>package lazycatTools.runtime; import org.eclipse.core.runtime.jobs.ISchedulingRule; public class SharedSchedulingRule implements ISchedulingRule { private final ISchedulingRule _rule; public SharedSchedulingRule(ISchedulingRule rule){ _rule=rule; } @Override public boolean contains(ISchedulingRule rule) { if(rule==this){ return true; } return _rule.contains(rule); } @Override public boolean isConflicting(ISchedulingRule rule) { if(rule==this){ return true; } return _rule.isConflicting(rule); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload