• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Locking - Notification issues

 
Bartender
Posts: 1872
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi everybody,
A few people on this forum recently noticed on the results forum that many people get a 44/80 score for locking and of course wonder why.
A thread on the topic was started here. There are so many possible reasons for loosing points in the locking part of the assignment that we couldn't find a definitive answer to that question. Those possible reasons included: support or not for deadlock detection, limitation or not of the number of locks grantable per client, the way update() and delete() ensure that the record was properly locked, the way lock cookies are generated if any, etc.
Here are a few recent such bad results in the locking part of the assignment:
Peter Ye: 44/80
Erin Bierer: 44/80
Nicholas Cheung: 44/80
Jmannu gundawar: 44/80
Damu Liu: 44/80
David L Wilson: 58/80 (an exception )
In Damu's thread, Max wrote:

Max:
I'd just to know if the people who lost points are people also didn't call notifyAll in lock.


Damu didn't reply but as I checked his code before it was removed, I may tell you that he didn't call notifyAll() in lock(), but well in unlock().
But Zak Nixon replied to Max:

I called notifyAll() in all of my object locking situations and still got counted off.


I concluded that he called notifyAll() in lock() and unlock() as Max suggests to do.
Nicholas called notifyAll() in both lock() and unlock().
Now if we compare with a recent good score in locking:
Trym Moeller: 80/80, Trym used notify() in unlock() (of course at the record level):

Trym:
Regarding the discussion about the locking 44/80 points:
I have focused on the requirement "while waiting using no CPU cycles". This I have accomplished calling notify instead of notifyAll.


And earlier, I remember that Vlad Rabkin got 80/80 for locking either. I know that he didn't call notifyAll() in lock() and I *think* he notified waiting threads at the record level either.
If we include Zak, out of 7 "bad" locking scores, we have:
notifyAll() in unlock() only: 1
notifyAll() in both lock() and unlock():2
don't know:4
While the 2 maximum scores for locking I give as examples above use notify() in unlock.
So it *could* be a correlation between the notification technique used and the scores for locking.
Hence this thread.
It would be great if people who passed could tell us here their score in locking and which technique below they used. Thank you in advance.
Instructions:
Any attempt to lock a resource that is already locked should cause the current thread to give up the CPU, consuming no CPU cycles until the desired resource becomes available.
I think that it's interesting to check how - and to what extent - the commonly used locking techniques fulfil that requirement.
Techniques used:
They fall in two categories:
  • Threads synchronize and wait on the locks container
  • Threads synchronize and wait on objects at the record level


  • The examples shown below focus on the notification issue, excluding other topics as deadlock detection, check of record validity and so on. And to keep it as simple as possible, I use here a regular HashMap as the locks container, mapping records to Data instances, I don't handle exceptions properly and I don't take possible lock cookies into account. And finally, neither lock() nor unlock() methods are made reentrant, though it's quite handy, especially for unlock().
    1.1 Synchronization on the locks container / notifyAll() in lock() and unlock():

    Comments:
    The fact that the locks container plays the monitor role forces the coder to call notifyAll().
    In unlock() it means that *all* threads are woken up (and consume CPU cycles BTW), even the ones which are not interested in the unlocked record.
    The call to notifyAll() in lock() is even worst because it's obvious that no waiting thread may be interested by a lock just granted. They wake up, check their waiting condition for nothing and return to sleep. With that design, each time a record is locked, all waiting threads consume (a lot of) CPU cycles uselessly, which breaks the requirements stated by the instructions. Notice also that I wrote the examples below just for the purpose of this thread ... without testing them.
    1.2 Synchronization on the locks container / notifyAll() in unlock() only:

    Comments:
    Better than 1.1, because CPU cycles are only wasted in unlock(). But still far from fulfilling the instructions perfectly.
    2 Synchronization and notification at the record level / notify() in unlock only:

    Comments:
    This is basically the technique I use myself (though my full LockManager class is a bit more complex than that - around 750 lines including JavaDoc comments instead of the 42 lines above ), the additional volume of code coming from the multi-tables support, the deadlocks and client crashes detection and the few additional helper classes and methods needed to achieve that.
    Notice that if your assignment includes a database lock (recNo == -1) - old assignments only I think -, solution 2 must be adapted while 1.1 and 1.2 still work.
    The big difference with solutions 1.1 and 1.2 is that waiting threads actually consume no CPU cycle "until the desired resource becomes available".
    A few days ago, I noticed that that design looks a bit like the Specific Notification Pattern by Peter Haggar (pattern that I didn't know when I coded my LockManager class BTW ).
    As all threads wait to be notified on their own monitor, a simple notify() is enough to wake them up, and *only* when the lock they are interested in becomes available. Of course, those individual monitors must be stored somewhere, and LinkedList is the perfect collection for that purpose:
  • it has the lowest overhead
  • it is naturally ordered


  • As you can see above, I chose to grant locks in FIFO order, which looks quite fair. But that "fairness" is just a free bonus.
    Further discussion:
    For many people, notify() has a bad reputation which brings some confusion. In this recent thread on this forum, someone wrote:

    People usually use notify() and ignore notifyAll() but I have a book it says notifyAll() is better than notify() and it mentions a phrase "miss notification", what does it mean? Is it ture? In the book, it says if there is only one thread waiting, notify() is OK, notifyAll() is OK but it uses more resource than notify(), that's a waste, but if there are more than one thread.


    Unfortunately, the book mentioned is not referenced. I replied this:


    You have three possible cases :
    1. You know that only one thread is waiting to be notified on a given object. In this case, notify() is better because more efficient.
    2. You know that multiple threads may be waiting to be notified on a given object, but all threads are interchangeable : you need one of them to perform a job, but it can be *any* of them. In this case, notify() is still better, for the same reasons.
    3. Multiple threads may be waiting to be notified on a given object, but they are not interchangeable for two possible reasons : they have a different interest in the notification (that's what happens with locking - each thread may be waiting on a different record lock (I thought there to solution 1.x of course ) or you need to awake all waiting threads for any reason of yours. In that case, notifyAll() *must* be used.


    I think that the "bad reputation" of notify() only comes from the fact that it's more subttle - hence dangerous - to use: OOH, in most of the cases, replacing a notify() by a notifyAll() will work while OTOH using notify() requires you to recognize that it is applicable to the context. And sometimes, using it correctly is more difficult either (In solution 2 above, you'll have noticed that the synchronized(lockObj) block is outside the synchronized(lockedRecords) one. Move it inside and the application will hang the first time a second lock claim will happen on a given record. ).
    A few days ago I found an interesting discussion on the Threads forum titled "notifyAll() instead of notify()", which included posters like Peter den Haan, Jim Yingst and Max Habibi, and an extract of Doug Lea's book: see here.
    In conclusion, it looks like it's difficult to find some unanimity on the topic, even among wellknown gurus and authors.
    And what about the graders opinions then ?
    So, whatever the technique you choose, I'd justify it. If you think that calling notifyAll() in lock() is a good practice (solution 1.1), tell it and try to explain why, bacause that call could not convince all graders. If you don't (solution 1.2), justify the missing notifyAll() line (even if it's not natural to document a blank line ), because your grader could expect that call.
    Regards,
    Phil.
     
    Ranch Hand
    Posts: 64
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Phil,
    The consistency of 44/80 is indeed alarming. I have initially finished but now going back to the drawing board again. But no regrets, learning more.
    I remembered Vlad finally chosen database synch instead of record level synch. In this thread, Vlad states (emphasis mine):

    I have decided to use #2/4 ...
    I will synchronize everything on one object this or mutex: All public methods (except getMetaData) in Data class. No more synchronized word in any other classes.


    Unless Vlad changed his design before submission, he "most likely" used notifyAll.
    My guess on this 44/80 score is the documentation of design, explicitly explaining why design is thread-safe and no deadlock is possible. Also the need to state explicitly the contract that client need to adhere for thread safety. It would help our 'investigation' if the candidates who got 44/80 can confirm if they have justified their design as being free of deadlock in their documentation.
    Another guess: 'defensive copying' - remember this?
    rgds,
    derek
     
    Ranch Hand
    Posts: 286
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Phil,
    Viva la France!
    Thanks for your article. I enjoyed reading it and then going to IBM
    to read their articles. And, I enjoyed learning about the new locking
    algorithm you presented.
    Concerning the related topic you raised,
    it seems you have defined two, basic types of locking mechanisms:
    1. those that use notifyAll() in the unlock method(),
    2. and those that use notify() in the unlock method()
    Have you asked, or do you know which type of locking mechanism
    Ken used?
    Thanks,
    Javini Javono
     
    Javini Javono
    Ranch Hand
    Posts: 286
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi,

    Notice that if your assignment includes a database lock (recNo == -1)...

    I think I've seen a posting dealing with a brand new assignment and it
    had database locking when recNo was passed in as -1.
    Can you outline or point to algorithms which deal with locking down
    the complete database?
    Here is an outline I came up with in my first draft. During this first
    draft design I was using one array of Mutex objects, where each element
    in the array represented one physical record in the file. To lock down
    the complete database, that is, so that no reads or writes would occur
    on it, I outlined the following steps:
    1. Each Mutex would have to be able to change it's behavior once its
    lock was no longer held and once it received an order (via the observer-
    observable pattern) to participate in locking down the complete database.
    2. To participate in locking down the complete database, each
    Mutex would refuse to let any thread through for any reason.
    3. Each Mutex would communicate to one master (again using the
    observer-observable pattern), and when the master
    noticed that all Mutex's were blocking every thread for every file
    record, then the master knew that the database was locked down (and
    implicitly knows that no threads are currently actively reading
    or writing any record).
    4. This next part I haven't work out very well yet, but either
    4a. The master would then operate on the file as a whole ignoring
    the Mutex's (since you don't need them since the database is locked
    down, and one thread can now do something, such as compact the
    database, or increase the size of the database, or whatever).
    4b. Perhaps each Mutex, while holding all other threads a bay, would
    allow a very unique, singleton type of thread to pass through and
    acquire the lock for as many records as it desired.
    A second outline is a little simpler.
    Before a thread is allowed to request a lock on an individual record,
    it has to pass through a Guardian object; the Guardian object either
    blocks all threads that attempt to pass through, or lets all threads
    pass through.
    So the steps here are a little simpler:
    1. The master tells the Guardian to start blocking all incoming threads.
    2. The master tells every Mutex for every record to let it know when it
    no longer has any threads waiting to process a record.
    3. When all Mutex's no longer have any threads waiting to process a record,
    and no new threads are entering because the Guardian is blocking them,
    the database is completely locked down.
    4. Again,the Guardian may be set up so that only a certain, unique,
    "magical" thread will be allowed to pass through, and this type of thread
    would carry out operations such as increasing the size of the file,
    compacting the database, and the like.
    So, is there a general pattern name for this, web articles, or books
    dealing with this topic?
    Thanks,
    Javini Javono
    [ February 09, 2004: Message edited by: Javini Javono ]
     
    Ranch Hand
    Posts: 451
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Phil,
    For the record, my solution, which scored 80/80, falls into your category 1.1 because it synchronizes first on the locks container and uses notifyAll() in lock() and unlock(). The following is from my design discussion thread.

    Locking
    ======
    The implementation of the Services book method in ServicesImpl is the only user of the locking API. I specified a contract in the Javadoc that calls for any potential users of the locking API to invoke lock/process/unlock as a sequence within the context of a single method call which guarantees it will happen within a single thread of execution. When lock is invoked, a check is made to see if the Contractor already has been locked by another thread. If it has, the current thread will wait until notified that it has been unlocked. If not, a Lock object is created that has a reference to the current thread and it is put in the locking Map with the Contractor as the key. The protected processing operation is performed and then the Lock is removed from the Map and destoyed by the unlock method call. It is vital that any exception that occurs during processing is caught and rethrown after the Contractor has been unlocked. Deadlock is prevented by specifying a locking order to be used by programmers.



    Regarding category 1.1, you state:

    The call to notifyAll() in lock() is even worst because it's obvious that no waiting thread may be interested by a lock just granted. They wake up, check their waiting condition for nothing and return to sleep. With that design, each time a record is locked, all waiting threads consume (a lot of) CPU cycles uselessly, which breaks the requirements stated by the instructions.


    While I think this argument about "breaks the requirements" may have some validity in reference to a category 2 design, I do not think it applies to category 1. In that situation, a thread waits on the locks container because it can't yet know whether the ultimate prize is indeed locked at that point in time. Once it gets the monitor on the locks container and it finds the resource locked, it waits and there is not a violation. The requirements state: "Any attempt to lock a resource that is already locked should cause the current thread to give up the CPU, consuming no CPU cycles until the desired resource becomes available." When the thread is notified, it starts all over again in its attempt to lock the resource so there is no violation, although there are some, not necessarily a lot of, wasted CPU cycles.
    On a practical note, it is hard to justify, IMO, the quest to eliminate every wasted CPU cycle by drilling down too far into the use of notify instead of notifyAll. The added difficulty in guaranteeing the reliability of the app and the large amount of time required to do so says to me it is a waste of resources to attempt to do so unless it can be shown that there is a demonstrable negative and unwanted effect on performance caused by the use of notifyAll(). According to Don Knuth, 2 of the worst software evils are "duplication" and "premature optimization". If no such demonstration has been made, the attempt to use notify() could easily fall into the latter category unless of course the reason for the quest is simply to learn in more detail how this stuff really works.

    Regarding:

    quote: people usually use notify() and ignore notifyAll() but I have a book it says notifyAll() is better than notify() and it mentions a phrase "miss notification", what does it mean? Is it ture? In the book, it says if there is only one thread waiting, notify() is OK, notifyAll() is OK but it uses more resource than notify(), that's a waste, but if there are more than one thread.
    Unfortunately, the book mentioned is not referenced.


    I think the book mentioned is one that I use, "Java Thread Programming" by Paul Hyde.
    [ February 10, 2004: Message edited by: Ken Krebs ]
     
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    By and large, I agree with Ken here. Also, my book very clearly recommends notifyAll over notify, as do most other detailed SCJD books, including RHE.
    All best,
    M
     
    Ranch Hand
    Posts: 493
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I used notifyAll() method in my unlock method code and got full points in the locking section.
    I also used client side locking instead of server side locking, but that shouldn't be the issue since Ken also got full-points on locking.
    The only thing that I can think of is did everyone submitting the assignment account for the physical file level locking (synchronization) in addition to the logical locking in the lock and unlock methods?
    My 2 cents.
    Bharat
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Derek,
    I didn't remember that Vlad changed his design on that point too (he changed so often ). But I *know* that he didn't call notifyAll() in lock() because he was the first guy here to point out the issue in Max's locking example and then Vlad argued a lot without never surrendering (see this thread). I remember that I agreed with him there, but Max didn't really entered into the discussion that time.
    BTW, while searching Vlad's thread, I found another one where Jim Yingst wrote:

    Suds, it's possible you're confusing acquiring and releasing a sync lock with lock()ing and unlock()ing a record. If one thread is trying to lock() a record which is already lock()ed by another thread, it's got to wait(). During this time, it really doesn't matter if other threads manage to lock() that record, or any other records. We only care if another thread manages to unlock() a record, because that might be the record we're interested in.
    In Max's code, notifyAll() after a lock() makes sense only if there are two different types of lock() (DB-level and record-level) and one type can override another. E.g. if thread A is waiting for a record-level lock() and thread B succeeds in getting a DB-level lock(), thread A may need to be notified to stop waiting for one record, since the whole DB is now locked. Or not, depending on your design. But in general, there's no use for notifyAll() inside lock(). The other threads are really just interested in knowing if unlock() as been accomplished, not lock().


    I still wonder what Jim meant with "and one type can override another" - it doesn't apply to our assignments anyway, but for the rest his position is very clear.
    Here is a thread (dated October 22) where I discovered (but forgot in the meantime) that Jim also uses record-level locking with a notify(), for the same reasons than I do : https://coderanch.com/t/184386/java-developer-SCJD/certification/Blocks-synchronized-but-without-wait .
    I also tried to find the thread where Max (a few weeks later than Vlad's thread above, because he'd been away in the meantime) conceded (I think to Jim) that notifyAll() in lock() was not needed in the context of our assignments. But unfortunately, I couldn't find it.

    My guess on this 44/80 score is the documentation of design, explicitly explaining why design is thread-safe and no deadlock is possible. Also the need to state explicitly the contract that client need to adhere for thread safety. It would help our 'investigation' if the candidates who got 44/80 can confirm if they have justified their design as being free of deadlock in their documentation.


    Proper documentation is indeed a must, and specially in the locking area as I suggested in my previous post.

    Another guess: 'defensive copying' - remember this?


    What do you mean exactly ?
    Regards,
    Phil.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Ken and Bharat,
    Thank you for your interesting replies.
    You both got the maximum score for locking, meaning that all three techniques may lead to a maximum score:
    Ken = 1.1 (notifyAll() in both lock() and unlock())
    Bharat = 1.2 (notifyAll() in unlock() only)
    Trym = 2 (notify() at the record level only)

    While I think this argument about "breaks the requirements" may have some validity in reference to a category 2 design, I do not think it applies to category 1. In that situation, a thread waits on the locks container because it can't yet know whether the ultimate prize is indeed locked at that point in time. Once it gets the monitor on the locks container and it finds the resource locked, it waits and there is not a violation. The requirements state: "Any attempt to lock a resource that is already locked should cause the current thread to give up the CPU, consuming no CPU cycles until the desired resource becomes available." When the thread is notified, it starts all over again in its attempt to lock the resource so there is no violation, although there are some, not necessarily a lot of, wasted CPU cycles.


    Ken, I have two basic questions for you:
    1. How did you justify the notifyAll() in lock()?
    2. In which area do you think that solution 2 above "break the requirements"? Which one, and to what extent?
    Best,
    Phil.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,

    By and large, I agree with Ken here. Also, my book very clearly recommends notifyAll over notify, as do most other detailed SCJD books, including RHE.


    Including the notifyAll() in lock()? Did you change your mind since October about it?
    I think it's really important to be precise about that. Hence I asked Ken above how he justified (I mean technically) his notifyAll() call in lock().
    A good score obtained with a given technique - especially if it's isolated as in this case (remember the two bad scores mentioned in my first thread) - cannot be an insurance for others : human grading being an imperfect process by nature, it's still possible to make a mistake and be lucky enough for not loosing any point for it .
    Best,
    Phil.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Javini,


    Concerning the related topic you raised,
    it seems you have defined two, basic types of locking mechanisms:
    1. those that use notifyAll() in the unlock method(),
    2. and those that use notify() in the unlock method()
    Have you asked, or do you know which type of locking mechanism
    Ken used?


    In the meantime, we know the technique Ken used, the one I presented as the worst case: notifyAll() in both lock() and unlock(). He got a perfect score for it, but I think you'd better wait for a *real* technical justification from him or from somebody else before doing as he did.
    Regards,
    Phil.
    PS: I couldn't find the time to reply to your second post, unfortunately. Though interesting, it's a little off-topic in the context of this thread I think. But maybe someone else...
    [ February 10, 2004: Message edited by: Philippe Maquet ]
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Philippe Maquet:
    Hi Max,

    Including the notifyAll() in lock()? Did you change your mind since October about it?


    Yes, about a great many things . However, not this one that I'm aware of. I think that, for certain types of designs, it's probably not horrible to use notify: However, in my experience, it's not the sort of thing I see a great many experienced Java developers do. Again, IMO, it's a bit sophomoric, like using Hungarian notation in Java code. It's just generally not the way things are done.
    For the SCJD, I don't believe it helps you in any way whatsoever to use notify().
    At best, IMO, the grader won't notice it, or will actually go the trouble of figuring out if your design actually works despite being somewhat unconventional. At worst, they will simply knock off some points without comment.
    AS


    I think it's really important to be precise about that. Hence I asked Ken above how he justified (I mean technically) his notifyAll() call in lock().


    Let me a little roundabout
    Bobby Fisher, the famous chess player, was asked what the best Chess opening was. He said "e4 is best by test". E4 is the most conventional of chess opening, with the king's pawn jumping forward two spaces.


    A good score obtained with a given technique - especially if it's isolated as in this case (remember the two bad scores mentioned in my first thread) - cannot be an insurance for others : human grading being an imperfect process by nature, it's still possible to make a mistake and be lucky enough for not loosing any point for it .
    Best,
    Phil.



    In all honesty, I think you and I disagree just how relevant an issue this is.
    M
     
    Ken Krebs
    Ranch Hand
    Posts: 451
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Phil,


    Ken, I have two basic questions for you:
    1. How did you justify the notifyAll() in lock()?
    2. In which area do you think that solution 2 above "break the requirements"? Which one, and to what extent?


    1. Sorry, but I'm fresh out of time for today and will have to wait until tomorrow to give you my answer. I hope you will find it interesting.
    2. I'm not saying that a type 2 solution breaks the requirements. I'm merely saying that a type 2 that uses notifyAll() may do so if the very strict interpretation of the requirements that you made is used by the grader. It is hard to be certain what interpretation a grader may make of this.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,

    In all honesty, I think you and I disagree just how relevant an issue this is.


    I think we should agree on an issue which *is* relevant for sure: why so many people fail in locking with a score of 44/80.
    As I wrote in my first post, we explored a few tracks in trying to explain it.
    The notification technique used is only one of them, and you started it yourself on the results forum by writing:
    I'd just to know if the people who lost points are people also didn't call notifyAll in lock.
    It let me think that the way people notify waiting threads for locks *could* be relevant to explain why people fail in locking, but it looks like it's a bit earlier to conclude yet.
    All design patterns describe a context, a problem and a solution, all three being understandable. No pattern is "self-justified", by general terms like "good practice" or "commonly accepted technique". I cannot understand such "arguments", but logical demonstrations, well-documented design patterns, language specs, etc. And as I generally don't believe in anything I cannot understand...
    Regards,
    Phil.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Ken,

    Phil,
    ---
    Ken, I have two basic questions for you:
    1. How did you justify the notifyAll() in lock()?
    2. In which area do you think that solution 2 above "break the requirements"? Which one, and to what extent?
    ---
    1. Sorry, but I'm fresh out of time for today and will have to wait until tomorrow to give you my answer. I hope you will find it interesting.
    2. I'm not saying that a type 2 solution breaks the requirements. I'm merely saying that a type 2 that uses notifyAll() may do so if the very strict interpretation of the requirements that you made is used by the grader. It is hard to be certain what interpretation a grader may make of this.


    1. For sure!
    2. notifyAll() in solution 2, where - by design - you *know* that one-and-only-one thread is waiting on the notified monitor?! Of course it won't hurt anybody, but it looks like telling someone: "I'll notify *all* passengers of that skateboard"! I guess I missed something in your reasoning. Could you explain it too?
    Best,
    Phil.
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Philippe Maquet:
    Hi Max,

    I think we should agree on an issue which *is* relevant for sure: why so many people fail in locking with a score of 44/80.
    As I wrote in my first post, we explored a few tracks in trying to explain it.
    The notification technique used is only one of them, and you started it yourself on the results forum by writing:
    I'd just to know if the people who lost points are people also didn't call notifyAll in lock.
    It let me think that the way people notify waiting threads for locks *could* be relevant to explain why people fail in locking, but it looks like it's a bit earlier to conclude yet.


    It's still an open question, I think, though I think it's probably not so important as you seem to.



    All design patterns describe a context, a problem and a solution, all three being understandable. No pattern is "self-justified", by general terms like "good practice" or "commonly accepted technique". I cannot understand such "arguments", but logical demonstrations, well-documented design patterns, language specs, etc. And as I generally don't believe in anything I cannot understand...
    Regards,
    Phil.


    lol
    I know exactly how you feel: I really do. I've often felt this way in various contexts. I think it's commendable attitude for an engineer.
    However, that being said, there are certain things that become tricks of the trade: a sort of nod that indicates that people know what they're doing, and the lack thereof indicates a certain lessened degree of familiarity.
    In different veins of life, you might notice this with dinner manners, appropriate dress, etiquette in a gym, etc.
    However, that's not particularly satisfying when you're the one who's disagreeing, is it?
    As I've indicated in our private exchange, notifyAll is more adaptable(in case you actually need to notify more then a single thread in the future), and it's just as efficient. That is, I would challenge you to even be able to measure the time between notifyAll and notify when a single thread is waiting.
    This leaves you with the 'skateboard' argument: however, I would counter that it's like saying 'all the riders of anything with wheels', which applies to skateboards as well as buses. Since the 'anything with wheels' statement(notfiyAll) is just as efficient as the 'skateboard' statement when all you get is skatebaord, yet adapts should a bus come by, it tends to be the preferred method. generally speaking.
    I also want to point out, IMO, that you're making too much of the 'wasted cycle' argument. IMO, the instructions are simply steering you away from a solution that uses a busy wait approach.
    All best,
    M
    [ February 10, 2004: Message edited by: Max Habibi ]
     
    Derek Canaan
    Ranch Hand
    Posts: 64
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Phil,

    [Phil]:But I *know* that he didn't call notifyAll() in lock() because he was the first guy here to point out the issue in Max's locking example.


    Yes, that is my understanding too that he did not issue a notifyAll in lock(), but my 'guesswork' from his posts showed that he did use notifyAll in unlock() as he synch at database level and not at record level.
    The post that Max conceded on notifyAll in lock() is found in this thread and i quote Max:

    I think you still get the 'win' here.


    Defensive copying is brought out by Vlad and Jim, in which the Data class'
    readRecord method returns to the client a copy of the record instead of the
    actual record reference itself. The danger of returning the actual reference is the client may do something with it that changes the states of the record reference without the proper locking protection. For example:This effectively also changes the name field of record 5 in the database cache.
    In this scenario, the system's locking mechanism is breached.
    rgds,
    derek
    [ February 10, 2004: Message edited by: Derek Canaan ]
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Derek,

    The post that Max conceded on notifyAll in lock() is found in this thread and i quote Max:


    Thanks so much! I understand why I couldn't find that thread: it was the third page of one of the thread I mentioned oabove myself and I didn't review it till its end...

    Defensive copying is brought out by Vlad and Jim, in which the Data class'
    readRecord method returns to the client a copy of the record instead of the
    actual record reference itself. The danger of returning the actual reference is the client may do something with it that changes the states of the record reference without the proper locking protection. For example:
    code:
    record = db.readRecord(5);
    record.setName("Sun");
    This effectively also changes the name field of record 5 in the database cache.
    In this scenario, the system's locking mechanism is breached.


    Good catch! And it was worth while to recall it. Thanks again.
    Regards,
    Phil.
     
    Derek Canaan
    Ranch Hand
    Posts: 64
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Phil,
    While you're online, would you mind taking a look at Question 8 of this thisthread. It is with regards to my analysis to notify vs notifyAll based on Andrew's comments. I'm still struggling with the issue.
    thanks,
    derek
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Derek,
    I was preparing an answer to Max's post above and performing a few tests. Unfortunately, I have to go and work now!
    I'll have a look at your thread at noon and will reply to you tonight (and to Max here either).
    Best,
    Phil.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi everybody,
    In a PM, Marlene pointed out a bug in my untested solution 2 above. Thanks again, Marlene, I stand corrected.
    In my actual LockManager implementation it was OK though (and tested!)...
    I'm lucky enough to have written that I didn't test those examples!
    Original version:

    Corrected version (though still not tested ):

    I'll correct the original code tonight to avoid confusion (though it will not be easy because of the UBB bug about posts edited with "quote" and "code" blocks ).
    Thanks again Marlene!
    Regards,
    Phil.
    [ February 11, 2004: Message edited by: Philippe Maquet ]
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Phil,
    You're nesting synchronized blocks? You are a rebel
    M
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Derek Canaan:
    Hi Phil, This effectively also changes the name field of record 5 in the database cache.
    In this scenario, the system's locking mechanism is breached.
    rgds,
    derek
    [ February 10, 2004: Message edited by: Derek Canaan ]



    This is somewhat out of context. In the post I simply told Vlad that since the topic had taken so long to explain, and since his observation was valid for the version of the SCJD he had, I though he had a valid point. the entire quote is

    quote:
    --------------------------------------------------------------------------------
    Originally posted by Vlad Rabkin:
    Hi Max and Jim,
    Finally, finally I understand it!!!
    Thank you !
    Vlad

    --------------------------------------------------------------------------------

    Well, since it's taken some 86 posts, I obviously didn't explain it very well . I think you still get the 'win' here
    M

    Further up in the Thread, I explain why you might use notifyAll for variations of the assignment that required locked the entire db.
    All best,
    M
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Philippe Maquet:
    Hi Derek,

    Good catch! And it was worth while to recall it. Thanks again.
    Regards,
    Phil.



    Just to bring another angle to this: This is actually a more robust implementation of locking then even Java's built in synchronization. My own, general rule for this sort of thing is : Don't.
    Unless there's a concrete problem, don't anticipate, optimize, or fix.
    This sort of thinking, btw, is why ArrayLists are to be preferred to Vectors. Vectors are actually more robust then ArrayLists, but they solve problems that, generally speaking, never occur.
    JMO,
    M
    [ February 11, 2004: Message edited by: Max Habibi ]
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,
    The issue pointed by Derek has nothing to do with locking but with cache write access as a side effect.
    Let me explain what I understood (and (very) quicly checked) : if your cache returns a *reference* to a record (array of Strings) *instead of a copy of it*, the caller may change any field value in the cache without any need to call updateRecord(). :

    Max, I think I have very good news for you!
    As you suggested me to make a test to compare performances of calling notify() vs notifyAll() on a monitor on which only one thread is waiting, of course I did and the differences are very low (and probably even implementation-specific): in that context, notify() is around 5% faster than notifyAll() on my Win XP machine. Hence not measurable.
    But I made another test which will make you happy I think: I compared the performances of the three techniques explained above, and the results are interesting:
    Here is my output:

    Solution 1.1 is the slowest
    Solution 1.2 is the fastest
    Solution 2 (which I expected to be the fastest) is in the middle
    On a scale of 100, it gives:

    Conclusions:
    1. I stand corrected, but now I know why!
    2. Solution 1.2 is definitely the best one: faster and much simpler
    3. Solution 1.1 is slower as we could expect as the notifyAll() in lock() wakes up all threads for nothing.
    I tried to find out why solution 2 is slower than 1.2:it's quite hard to tell for sure, but I guess that it comes from the overhead of managing the temporary LnkedLists...
    All best,
    Phil.
    PS1: The nested lock you pointed out was not an issue. I try to avoid them of course, but here it was "under control".
    PS2: Derek, I don't forget you, but unfortunately my tests took much more time than expected and I have to stop tonight (I have a class early in the morning tomorrow). I'll go to the thread you pointed tomorrow night.
    [ February 11, 2004: Message edited by: Philippe Maquet ]
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Philippe Maquet:
    Hi Max,
    The issue pointed by Derek has nothing to do with locking but with cache write access as a side effect.


    I understand Derek's issue Phil: he's providing a Threadsafe implemention of the returned value. I'm just suggesting that this isn't really necessary, and doesn't actually solve a problem.
    The context I was using to back up this claim was the Vector class, which also provides a ThreadSafe implemention, yet isn't really to be preferred, except under very, very specific circumstances.

    Regarding your tests: I have a suggestion for you that may seem a little strange, but bear with me. Try running the tests in a different order, and see if get different results.
    M
     
    Ranch Hand
    Posts: 108
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Dear Philippe, i don't believe you are still here sorting out for everybody's sake. Now, you are just perfectionist. Never mind...
    Just to make my small contribution.
    I've got full points for locking:
    1.used LockManager that orchestrated locking of records as a whole in BitSet data structure
    2. could add records dynamically because LockManager made sure that everybody waited until new rec. was added (otherwise you easily get deadlock);
    3. used notifyAll, it's simply stated that notify is too risky;
    4. used Semaphore style locking management for individual records,
    meaning that individual rec.s checked the thread's identity on their own and coordinated their lock-wait states, LockManager was satisfied with knowing their state such as unlocked(0) locked(1) in its collection to decide what to do next.
    5. Used two LockManagers - one for client side locking (if they wanted) and another was performed automatically by the database's wrapper - abs. same mechanism but methods were private.
    like method modify for ex.

    lock(int i) -> called by client but can be omitted (still cant corrupt my rec)
    internalLock(int i) -> called by db
    ----
    modify
    blah-blah
    ----
    internalUnlock
    unlock

    5. tested extensively and in docs rather boldly mentioned that it impossible to get deadlocked in the setup and moreover clients can't workaround db's internal locking since all methods are padded, but if it wanted to be protected from other client (dirty reads or whatever) it is advised to use public lock-unlock methods provided for the purpose.
    Probably it's possible to deadlock, but i bombarded the db with requests at random intervals from 0-4 millisecs overnight - everything was clear.
    Hope i was more or less clear - in a hurry

    [ February 11, 2004: Message edited by: Svetlana Koshkina ]
     
    Ken Krebs
    Ranch Hand
    Posts: 451
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Phil,


    Ken, I have two basic questions for you:
    1. How did you justify the notifyAll() in lock()?
    2. In which area do you think that solution 2 above "break the requirements"? Which one, and to what extent?
    ---
    1. Sorry, but I'm fresh out of time for today and will have to wait until tomorrow to give you my answer. I hope you will find it interesting.
    2. I'm not saying that a type 2 solution breaks the requirements. I'm merely saying that a type 2 that uses notifyAll() may do so if the very strict interpretation of the requirements that you made is used by the grader. It is hard to be certain what interpretation a grader may make of this.
    1. For sure!
    2. notifyAll() in solution 2, where - by design - you *know* that one-and-only-one thread is waiting on the notified monitor?! Of course it won't hurt anybody, but it looks like telling someone: "I'll notify *all* passengers of that skateboard"! I guess I missed something in your reasoning. Could you explain it too?
    Best,
    Phil.


    Responding to your response to my response to your response to my response ... whew, getting dizzy. :roll:
    I'll try again.
    1.
    My original claim was that solution 1 didn't make a technical break of the requirements as you stated it might:

    The call to notifyAll() in lock() is even worst because it's obvious that no waiting thread may be interested by a lock just granted. They wake up, check their waiting condition for nothing and return to sleep. With that design, each time a record is locked, all waiting threads consume (a lot of) CPU cycles uselessly, which breaks the requirements stated by the instructions.


    My defense:

    ...I do not think it ( i.e. "breaks the requirments") applies to category 1. In that situation, a thread waits on the locks container because it can't yet know whether the ultimate prize is indeed locked at that point in time. Once it gets the monitor on the locks container and it finds the resource locked, it waits and there is not a violation. The requirements state: "Any attempt to lock a resource that is already locked should cause the current thread to give up the CPU, consuming no CPU cycles until the desired resource becomes available." When the thread is notified, it starts all over again in its attempt to lock the resource so there is no violation, although there are some, not necessarily a lot of, wasted CPU cycles.


    So my justification is that my solution is technically correct and works reliably. So what if it wastes a few cycles. The attempt to eliminate them is not worth the risk and the time needed to show it still is correct and reliable, IMO.
    My point here is that if this were a real situation and I was the boss and you were doing the work and I thought you were wasting time/money and possibly compromising the reliability of the program by trying to squeeze a few extra, unnoticeable cycles out of it, I might take a dim view of it, especially considering that it is basically a trial version as stated in the instructions.
    I agree with Max:
    "Unless there's a concrete problem, don't anticipate, optimize, or fix."
    2.
    I was just trying to say that your argument about breaking the requirements by wasting a few cycles may have some merit if notifyAll is used instead of notify for a solution 2 type of implementation. I wasn't trying to argue against using solution 2.
    Hope that clears it up.
    [ February 11, 2004: Message edited by: Ken Krebs ]
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,

    I understand Derek's issue Phil: he's providing a Threadsafe implemention of the returned value. I'm just suggesting that this isn't really necessary, and doesn't actually solve a problem.


    I wrote above that Derek's issue had nothing to do with locking. I should have added and with threads neither.
    Data should protect his cache, period.
    Without a cache:
    ---------------
    - Data reads a record in the file
    - Data converts it in a *new* array of String (hence referenced locally)
    - Data returns it to the caller
    - If the caller changes any element in that array, it has no effect on subsequent reads.
    With a Cache: (badly implemented as Derek points out)
    ------------
    - Data returns a *reference* to a cached record.
    - If the caller changes any element in that array, it changes the cache contents, and subsequent reads don't get the same record.
    The fact that the Data designer/coder decides to implement a cache or not shouldn't have any side effect IMO.

    Regarding your tests: I have a suggestion for you that may seem a little strange, but bear with me. Try running the tests in a different order, and see if get different results.


    I get very comparable results. Now I noticed that with any order, there is small variations among them which is probably due to the random nature of such tests.
    All best,
    Phil.
    [ February 12, 2004: Message edited by: Philippe Maquet ]
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Ken,
    Thank you for your reply.
    2. OK
    1. Your answer just demonstrated that twice you didn't understand my question, but it's not a problem anymore. I was questioning 1.1 (notifyAll() in lock() and unlock()) and you defended 1.2 (notifyAll() in unlock() only), solution I find now the best one as explained above.
    Regards,
    Phil.
    [ February 11, 2004: Message edited by: Philippe Maquet ]
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Svetlana,
    Nice to see you!


    Dear Philippe, i don't believe you are still here sorting out for everybody's sake.
    Now, you are just perfectionist. Never mind...


    ... though a thread like this one looks more like masochism than perfectionism!
    I remember your locking design and found it very smart. I think you're the only person on this forum who ever achieved locking that way.
    I'll probably add it to my tests suite of locking solutions, by technical curiousity. I wouldn't be surprised that it's the fastest, but as I just promised myself from now to believe only what I can check and see (see my "disappointing test results above" )...
    Best regards,
    Phil.
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Philippe Maquet:
    Hi Max,
    quote:
    --------------------------------------------------------------------------------
    I understand Derek's issue Phil: he's providing a Threadsafe implemention of the returned value. I'm just suggesting that this isn't really necessary, and doesn't actually solve a problem.
    --------------------------------------------------------------------------------
    I wrote above that Derek's issue had nothing had nothing to do with locking. I should have added and with threads neither.
    Data should protect his cache, period.


    This is an interesting assertion. Does a hashmap, even a synchronized one, protect it's cache, period?


    quote:
    --------------------------------------------------------------------------------
    Regarding your tests: I have a suggestion for you that may seem a little strange, but bear with me. Try running the tests in a different order, and see if get different results.
    --------------------------------------------------------------------------------
    I get very comparable results. Now I noticed that with any order, there is small variations among them which is probably due to the random nature of such tests.



    This is also interesting, and not consistent with my own data. What version of the jdk are you using? Can you simplify your tests, and post them? No need to provide locking code per se: any code the compares notify, notifyAll, or notifyAll + notifyAll will do.
    M
     
    Ken Krebs
    Ranch Hand
    Posts: 451
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Phil,

    1. Your answer just demonstrated that twice you didn't understand my question, but it's not a problem anymore. I was questioning 1.1 (notifyAll() in lock() and unlock()) and you defended 1.2 (notifyAll() in unlock() only), solution I find now the best one as explained above.


    I thought I was defending 1.1 but what I didn't understand is that you were looking for a purely technical argument as to why 1.1 is objectively better than 1.2, in other words, is there a possibility for a potential failure for 1.2 ?
    My answer is, I haven't been able to find one for the code that is described here. There is a possibility for failures using a similar but not identical approach so those attempting to use this technique should do so with sufficient caution.
    Thanks a lot for shedding so much light on this difficult subject with your excellent analysis and profiling. It helped me to acquire a much better understanding of the potential hazards and opportunities when writing this sort of code.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,

    This is an interesting assertion. Does a hashmap, even a synchronized one, protect it's cache, period?


    I don't understand what you mean. I think we don't talk about the same topic! I just focused on what Derek pointed out and I agree with him.
    He returns a copy of a cached record instead of a reference to it. Nothing more than that and I find it safer. I applied a similar technique in another context (see here): you may have a look at the AbstractSettings class and its JavaDoc comments, it's the same reasoning.

    This is also interesting, and not consistent with my own data. What version of the jdk are you using? Can you simplify your tests, and post them? No need to provide locking code per se: any code the compares notify, notifyAll, or notifyAll + notifyAll will do.


    I was talking about the second test (the one which elulated locks granting with the three techniques explained above). But'll I send you both of them anyway, but preferably this weekend (I'm dead-tired tonight ). I have to write to you anyway.
    Best,
    Phil.
    [ February 12, 2004: Message edited by: Philippe Maquet ]
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Philippe Maquet:
    Hi Max,
    quote:
    --------------------------------------------------------------------------------
    This is an interesting assertion. Does a hashmap, even a synchronized one, protect it's cache, period?
    --------------------------------------------------------------------------------
    I don't understand what you mean. I think we don't talk about the same topic! I just focused on what Derek pointed out and I agree with him.


    Let me ask the question in a different way. What is the principle that leads you to believe that Data should protect it's cache? What existing problem are you solving here? And does that principle cause a conflict when applied to, say, a HashMap?


    quote:
    --------------------------------------------------------------------------------
    This is also interesting, and not consistent with my own data. What version of the jdk are you using? Can you simplify your tests, and post them? No need to provide locking code per se: any code the compares notify, notifyAll, or notifyAll + notifyAll will do.
    --------------------------------------------------------------------------------
    I was talking about the second test (the one which elulated locks granting with the three techniques explained above). But'll I send you both of them anyway, but preferably this weekend (I'm dead-tired tonight ). I have to write to you anyway.
    Best,
    Phil.



    Looking forward to it.
    Best regards,
    M
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,

    Let me ask the question in a different way. What is the principle that leads you to believe that Data should protect it's cache? What existing problem are you solving here? And does that principle cause a conflict when applied to, say, a HashMap?


    What I don't understand (yet? ) is why you come back over again with that HashMap...
    OK, a cache needs some internal container, and it can be an HashMap but an ArrayList, a Vector or even a HashTable could do the job depending on your design (need for a List or a Map) and the built-in thread safety that you want or not. The issue is elsewhere.

    What is the principle that leads you to believe that Data should protect it's cache?


    Data has a public interface (DBAccess) which specifies the ways data (lowercased on purpose ) may be accessed, mainly through methods:
    - String[] readRecord(long recNo)
    - updateRecord(long recNo, String[] data)
    - deleteRecord(long recNo)
    The only valid/official ways of altering data are calls to updateRecord() and deleteRecord() (I forget createRecord() for the sake of simplicity).
    Those are the *specifications* (interface).
    Now you as the coder, you have an *implementation* decision to make: reading records from the file or possibly reading them from a cache to be implemented.
    What Derek pointed out is that such an implementation decision should have no effect on what people may do or not with the record they receive from readRecord().
    1. If it is read from the file:
    You'll first read the record in some bytes array (or a ByteBuffer, whatever), and then convert it in an array of field values.
    By definition, that String[] will be a new one, constructed *locally* somewhere, just for the caller's usage (no other caller will never have any chance to reference the *same* array).
    That caller (C1) may change any element of that "private" copy of the record, maybe to prepare an updateRecord() BTW.
    Now another caller C2 reads the same record through readRecord(): it will get another "private" copy of the *original* record (if C1 didn't called updateRecord() yet) and ... that's perfect. Exactly what we should expect.
    2. If it is read from a cache (whatever the container):
    If you simply return the reference to a record in the cache, the scenario is quite different:
    C1 changes a field value (an element of the array) to prepare an update.
    C2 calls readRecord() and, as the record is in cache, gets a reference to the same array C1 is changing. Is it still what you would expect from the system ?
    Derek's solution is simple: when a record must be returned from the cache, a reference to a *copy* of the record array is returned instead of a reference to the original record in cache.
    Now the scenario is exactly the same as the one described in 1 above.
    Is that clearer ? Not sure because I'm falling asleep...
    Best regards,
    Phil.
    [ February 12, 2004: Message edited by: Philippe Maquet ]
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I understand Derek's point Phil, but I'm not sure you understand mine: I think this because you're making an interesting, but non relevent, point .
    Please review the question again, because this is integral to the lesson being taught by the SCJD.
    What actual, existing, problem are you solving here?
    What actual, existing, requirement are you implementing?
    If you didn't go to this extra effort, what part of your assignment would you be failing?
    M
     
    Javini Javono
    Ranch Hand
    Posts: 286
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi,

    There are so many possible reasons for loosing points in the locking part of the
    assignment that we couldn't find a definitive answer to that question. Those
    possible reasons included:
    * support or not for deadlock detection,
    * limitation or not of the number of locks grantable per client,
    * the way update() and delete() ensure that the record was properly locked,
    * the way lock cookies are generated if any,
    * etc.

    Sun adds these comments:
    Locking: This category covers your implementation of the locking scheme which
    is described in detail in your assignment documentation. This part of your
    assignment deals with multi-threaded coding.
    and Sun says this:
    Therefore, you should expect to create a fairly simple user interface, a network
    protocol with associated client and server code, a multi-user server that
    accesses flat files on the local file system and

    provides for a locking mechanism to protect the integrity of the data in the
    face of concurrent access from multiple clients.

    We probably still have quite a lot of unknowns to consider. For instance, did
    each individual exactly follow the DBMain interface? There have been
    some postings, I think, were people stated they changed the method signatures
    somewhat and received full credit (but this is no guarantee any future person
    will).
    Probably the major criteria, which usually are explicitly stated are the
    following (and, of course, I'm guessing to some extent):
    1. The server must be reasonably responsive. Thus, to code a solution like
    this:

    we'll assume creates a server which does not support concurrency enough. This
    is the worst case design.
    2. Also related to concurrency, is how reads are conducted. The server should
    be able to be designed with a minimal MUTEX (mutually exclusive lock for all reads
    and writes) in addition to simple business read method not being required to
    call guard.lock() and guard.unlock(). That is, only business methods which
    mutate a record would consider using guard.lock() and guard.unlock().
    3. I don't think that it is required that the locking mechanism be set-up so
    that you have exclusive writes and free reads (or whatever this terminology
    is). I'd have to do testing to some extend to see what this might buy you with
    respect to increased speed and increased concurrency.
    4. Note Sun's comments under Locking above: "This part of your assignment
    deals with multi-threaded coding." So, it is perfectly believable that those
    that got a bad score may have, if I may guess, had one instance of Data on their
    server, so unknown to them, since it wasn't found during testing, there is a
    flaw in that somewhere in their multi-threaded code, and not necessarily in the
    lock itself, it is not threadsafe, and wham, 40 points are gone. To test this
    hypothesis, I'd guess the following: those that used a factory and parceled
    one unique Data on the server per client, had no multi-threading issues (except
    in the lock itself), thus, the probability of a high score was good. Those that
    had one instance of Data on the server, were faced with multi-threading, and if
    it is new to you, its very, very easy to make a mistake, and then not have it
    turn up in testing: 40 points gone.
    In broad stokes, with a little speculation, I think item 4 is now the most
    probably suspect. I guess you'll need another poll!
    Thanks,
    Javini Javono
     
    Derek Canaan
    Ranch Hand
    Posts: 64
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max and Phil,
    Felt a bit bad for starting a war , but, may i join in?

    [Max]: What actual, existing, problem are you solving here?
    What actual, existing, requirement are you implementing?


    First, i think defensive copying, implemented or not is defendable.
    In a cached-design, it's defendable because it makes the data consistent, and the Data class thread-safe. It's the same reason why we implement createRecord() in a thread-safe manner even though we are not explicitly asked to (gui only updates and searches), so that when a client invokes Data.createRecord(), we will not get data inconsistency especially for design that re-uses deleted records.
    Furthermore, this gives system consistency. We have a thread-safe readRecord() and a thread-safe createRecord(). Clients are not able to mess up my data when they invoke Data.readRecord() or Data.createRecord(). My assignment states that "marks are awarded for a clear and consistent approach.
    Lastly, i want to be protecetd from an assessor who may say, "Hey, i can see a security hole here. The system's locking mechanism is breached." Hence, i choose to err on the safer side.
    rgds,
    derek
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Javini,


    (...) 40 points are gone. To test this
    hypothesis, I'd guess the following: those that used a factory and parceled
    one unique Data on the server per client, had no multi-threading issues (except in the lock itself), thus, the probability of a high score was good. Those that had one instance of Data on the server, were faced with multi-threading, and if it is new to you, its very, very easy to make a mistake, and then not have it turn up in testing: 40 points gone.


    Interesting, but I'd add to "except in the lock itself" "and access to the file".
    Regards,
    Phil.
    reply
      Bookmark Topic Watch Topic
    • New Topic