Gregg Wonderly wrote:
I'm thinking more and more that this runAfter business should really be
a O(1) thing. If runAfter() is implemented to return true, can not that
decision be made at the time of enqueue? It just looks like so much of
this is spread around to so few actual users in the current API. As I
look over the usages, there is some fuzzy logic involved in some cases
where the sequence number is used for ordering. In a couple of other
cases, there is a specific task that another task wants to run after.
I am generally concerned about sequence number ordering, not just at
this level but in reading about Jini generally. The problem is what to
do if message n has already been processed when message m, m<n, arrives.
In the end, it seems like the API should be
something more like
TaskManager.addAfter( Task myTask, Task dependedOnTask );
That does not cover all the current cases, and would require multiple
modules to keep their own Task lists and do the scans themselves.
For example, with the current system we can deal with
many-reader-single-writer rules. A write has to wait for any access to
the same "address". A read has to wait for writes to the same "address".
Reads do not have to wait for each other.
and, there would be something like
TaskManager.addWithSequencer( Task myTask, new Sequencer() {
public void runAfter( List<Task> lst, int cnt ) {
// search lst and return result
}
});
So that simple checks can be made for "hasSequencer". The first
case could just result in a call like
TaskManager.addWithSequencer( Task myTask, new Sequencer() {
public void runAfter( List<Task> lst, int cnt ) {
int idx = lst.indexOf( dependedOnTask );
return idx >= 0 && idx < cnt;
}
});
This would remove all of the abiguity and calling into the "return
false" implementations. You could tell at the point of the add(), what
sequencing was going to apply, no searching back into the implementation
class.
The more I think about it the more I feel TaskManager represents a
coincidental coupling between two logically distinct modules:
1. A sequencer whose business is determining whether a task is runnable.
2. A thread pool whose business is allocation of runnable tasks to threads.
Separating these functions might allow different types of sequencer,
including a trivial one for cases in which there are no sequencing
requirements. In some cases, tasks may be being allocated to the same
TaskManager to share a thread pool even if they have no sequence
relationship.
That said, some of the code I've read suggests that I can probably make
the sequencing more efficient by putting it in a utility module than it
would be if every using module implemented it separately.
Patricia