- A Concurrent Affair - https://www.concurrentaffair.org -

Storage Considerations

After getting several things to work in the early morning ours yesterday, I’ve been cleaning a few things up in the thread monitor source. One thing I’m working on is the unification of the data representation.

Since I want as little interaction between the master and the slave as possible and since I want to avoid JPDA’s idiosyncracies regarding access and thread state — and also for pure ease of use (accessing remote data using JPDA is more than cumbersome) , I am duplicating some of the data structures and translating the data I get from JPDA mirrors.

There are basically two separate lists: a list of threads, and a list of locks (Java calls then monitors; so far, I have chosen to call them locks in order to avoid confusion between these monitors and my monitors — ThreadMonitor, ObjectMonitor, SynchronizedMonitor; I should probably do a thorough renaming of all the terms to get my terms in line with the rest of the world).

An entry in the thread list contains a list of owned locks and a reference to the currently contested lock. An entry in the lock list contains a list of waiting threads and a reference to the owning thread. It’s something like this (pseudocode):

1
2
3
4
5
6
7
8
9
class ThreadInfo {
    List<LockInfo> ownedLocks;
    LockInfo contestedLock;
}

class LockInfo {
    List<ThreadInfo> waitingThreads;
    ThreadInfo owningThread;
}

Now, when I change a value in one of the locks, for example, that change needs to be reflected in all the thread entries that somehow contain that lock, either as owned or as contested lock.

Right now, I’m going through the entries and I update them, but I’m not convinced I actually keep all of them up-to-date. I also think this might get complicated later. Conceptually, the thread and lock information should be singletons per unique ID, so maybe I should actually model it that way: The actual entries are stored in hash maps indexed by the unique ID, and the entries only store unique IDs that serve as indices into the hash maps:

1
2
3
4
5
6
7
8
9
10
11
12
class ThreadInfo {
    List<Long> ownedLockIds;
    Long [1] contestedLockId;
}

class LockInfo {
    List<Long> waitingThreadsIds;
    Long [1] owningThreadId;
}

HashMap<Long [1], ThreadInfo> threads;
HashMap<Long [1], LockInfo> locks;

Of course, this makes lookups a bit more tedious. You absolutely have to keep track of both lists separately, even if you don’t really care all that much for data consistency. A hybrid approach would be to store the thread and lock information in a hashmap, and also store copies in the entries but allow them to be out-of-date. Access to up-to-date information can be obtained through the hash map and the unique ID stored in the entry to be looked up:

1
2
3
4
5
6
7
8
9
10
11
12
class ThreadInfo {
    List<LockInfo> ownedLocks;
    LockInfo contestedLock;
}

class LockInfo {
    List<ThreadInfo> waitingThreads;
    ThreadInfo owningThread;
}

HashMap<Long [1], ThreadInfo> threads;
HashMap<Long [1], LockInfo> locks;

This approach tries to get the best of both worlds, but it incurs higher memory use and is possibly more confusing because there is more than one way to do something.

Any thoughts?

Update

I’ve decided to go with a variant of the second model. There’s just less that can go wrong with this implementation. I have changed the lists to sets, though. Each id should be present only once, so a set is a more natural representation here. The data structure now looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
class ThreadInfo {
    Set<Long> ownedLockIds;
    Long [1] contestedLockId;
}

class LockInfo {
    Set<Long> waitingThreadsIds;
    Long [1] owningThreadId;
}

HashMap<Long [1], ThreadInfo> threads;
HashMap<Long [1], LockInfo> locks;
[2] [3]Share [4]