I have a strange synchronization architecture going on and I am looking for an elegant solution. I already had a solution but I cannot say anything about its validity and its also a little ugly. So here is the problem, hopefully someone can help me.
There are 2 groups of tasks that can be started and run. Each task has its own thread. These two groups both extend from one super class that takes care of the synchronization part. I will call these two groups Group A and Group B for simplicity.
Conditions:
-
If there are only Group B tasks running then they can run at the same time and they do not interfere with each other.
-
If a Group A task is started* then the constructor for a Group B task should fail with an exception. Any number of Group A tasks can be created even if a Group A task is already running
-
A Group A task cannot execute until all current Group B tasks have finished. (* from above)
-
Only 1 Group A task can run at a time. They must be queued. (It is optional to block Group B tasks between the running of the two Group A tasks as long as the previous conditions still apply)
I believe my method works but I do not like the way it works because of the many different synchronization points that it uses, and the fact that I have a sleep while waiting for the counter. Anyway the code is below
public abstract class LockableTask<Params> extends AsyncTask {
private final boolean groupA;
private static Boolean locked = false;
private static final Semaphore semLock = new Semaphore(1);
private static int count = 0;
public LockableTask(boolean groupA) {
this.groupA = groupA;
synchronized (locked) {
if (locked && !groupA) {
throw new InputException("We are locked, please wait");
}
}
}
@Override
protected final AsyncReturn doInBackground(Params... params) {
if (!groupA) {
synchronized (locked) {
count++;
}
}
try {
if (groupA) {
semLock.acquireUninterruptibly();
synchronized (locked) {
locked = true;
}
while (true) {
synchronized (locked) {
if (count == 0) {
break;
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
return runInBackground(params);
} finally {
synchronized (locked) {
if (groupA) {
locked = false;
} else {
count--;
}
}
if (groupA) {
semLock.release();
}
}
}
protected abstract AsyncReturn runInBackground(Params... params);
}
If someone has a nicer solution even if just barely nicer that would be great
If you have an upper bound on the number of simultaneous Group B tasks (even if it’s very large), you should be able to achieve the rules you describe with a single semaphore.
Group A task:
Group B task:
End result: whilst any group B are running, A cannot acquire the number of permits it requires. Once it does, no B can acquire a permit and neither can any other A.