Originally posted by Jon Entwistle:
Hi Anton,
[qb] That's how I read it at first: that only one client at a time can access the file.
Please tell me if I am wrong but I think that this is where you might be having problems conceptualy with this - the client actualy never accesses the file. The only thing any client does is to ask the server to access the file on its behalf - it is up to the server to manage these requests from any number of concurrent clients, which it must do without violating the record (i.e. cookie) locking contract and without corrupting the file or any shared variables.
Regards,
Jon[/QB]
Sure, what I meant is that the Data class accesses the file. And I thought that each Data object has to be able to access the file concurrently, i.e., lock a portion of the file via a static controlling Map, say, in the Data class, and then write to the locked region. I mean, using RandomAccessFile it can be accomplished concurrently.
What I now think I should do is just specify in the static Map structure that a certain record is being used so that another Data object would not proceed to modify it at the same time. All the locking is in that Map structure. In that way, all I have to do is synchronize on the Map, try to see if a record is already locked and wait until it is no longer locked, put the record number back in, and proceed to modify it.
Here's a couple of questions I asked myself:
1) Do I need to use a synchronized Collection, or can I get away with unsynchronized one?
I think I will get a synchronized one. Even though when writing to the Map I will have a lock, I don't want anyone who later changes my program to accidentally make it thread-unsafe.
2) Do I need to make the Data class thread-safe?
I think I do, since I found out that each RMI thread may access whichever Data object it pleases.
3) How do I avoid a deadlock?
I will avoid a deadlock by making sure I never reverse-nest locking. I will nest locking in the same order, though:
i) thread has to acquire lock on Data object.
ii) thread has to acquire lock on Map from locked Data object.
However, all threads will undergo this procedure in the same order so there should not be a problem.
I will:
a) Synchronize the Map. (threads may wait on that.)
b) Check for record being locked in it. If yes, wait() (lock on Map released.)
c) When the record is unlocked, lock it again, this time by different Data object using the calling thread (i.e. client) lockCookie value. When checking, Data object acquires lock on Map automatically. Release lock on Map.
d) Modify the database file. Here I must guarantee that a record is unlocked only after database file is modified.
e) Synchronize the Map. Unlock the record I locked.
So in order to accomplish database access safely, three locks are in play:
lock 1 is the lock on the Data object.
lock 2 is the lock on the Map.
lock 3 is the lock put in the Map. This lock is purely logical: no lock is granted in the sense the synchronized keyword grants it. But this lock is accomplished by a Record object in the Map (or Set, perhaps, to avoid checking for duplicates) as the key (and lockCookie as the value) so that other threads, checking upon the presence of this record number, would abstain from modifying the record in the database.
That's basically what I came up with in regard to locking. I'd like also to keep an eye on records with the Record class. Each instance would have the beginning and end of the record byte region and static info on the header and such; and also instance methods to strip or add whitespace, etc. Each record would not have to be synchronized. This way, the functionality in Record could be modified to handle other database formats easily.
But here's the question I have not answered yet. When I need to search the entire file, what is the most efficient way to handle it? I could put a null value for the key and have threads check on it, and if it exists, wait, so that all logical locks clear before a thread proceeds to search the whole file. In this case, the find feature is going to be pretty time-consuming, and I am not sure it's a good choice. But I don't really see any other way.
The only other way, though, is to have the File object locked by a thread before modyfying it. That way, a find request will not need to lock anything. It needs only to acquire a lock on the File object before searching. Other threads, even those which have locked a Record, will simply wait before accessing and modifying the file. However, again, there is the issue that the file will be searched before being immediately modified, and the client may be getting outdated information. So, I think I am left to the time-consuming null lock-clearing scheme.
[ July 27, 2004: Message edited by: Anton Golovin ]