Fog Creek Software
Discussion Board

CityDesk Entity Classes - locking strategy?

Ok, just taking a quick read of Joel’s class objects on CityDesk, I see nothing about a record locking strategy.

What happens when two users try to edit/load the same article?

I of course also tend to create a set of objects for most applications that handles complex udaates..and of course more importantly centralizes the update code.

I also usually add:


The above is a Boolean, and thus:

MyObject.LoadId  = 222

So, now we have a record id of 222 set. The property LoadId of course can thus be both set, or viewed.

When doing some critical operations, I do

If MyObject.MyLock = true then
  ‘ bla bal al
  ‘ ok..user cancled
end if

Note that IF the object/record entity in this case IS LOCKED, then I pop up a nice screen showing the user name, and workstation (pc name) that currently has the lock. It also has a timer event, and if the user that is holding the lock  exits, then the above small prompt screen automatically closes and continues (the above will thus return true). However, there is a cancel on that small “locked” prompt form. If the user hits cancel, then the above MyLock thus returns false.

In your examples you did not mention any kind of record/article locking strategy?

Do you have a record locking strategy along with your objects/Entity examples?

Any neat ideas…or have you dealt with this problem yet?

To everyone:

What do most of you folks do out there for record lock type strategy?

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Saturday, January 4, 2003

OK, brief summary of Transaction Requirements:

ACID: Atomic, Consistant Isolated,Durable

Locking Does not get you Atomic, as your code needs to do that.
Locking does not get you consistant, that has to be enforced elsewhere
Locking gets you Isolated.  IE, I can't get at your record until you are done with it.
Locking does not get you Durable, as your changes may be lost dues to system failure etc.

If you want all of this, use a transaction monitor.  JBoss for EJBs is free and does this, but you have to use entity beans to get it.

Microsoft Transaction server does this for MS technologies.  At least, that was the technology last I looked.  Only problem there is that it had to be stateless.

Locking without serious thought can lead quite easily to deadlocks.  I know, I do it all the time.

Adam Young
Saturday, January 4, 2003

Well, we can assume that the data engine we are using has transactions.

It solves nothing from a user interface point of view does it?

Two people want to update the same word document.

You have to serialize this process.

We can get into a big discussion about how atomic a value is here, but that is not really our problem.  In general, we do NOT have the technology to update partial parts of a word document. The same goes for the average application. It is not a record, but usually a group of records that we must deal with.

So, we are stuck back to square one…and ACID means nothing again!!

City desk is a document management system, and that is what the examples we are talking about apply to. However, my example was in fact a tour object from a reservation system (been writing those things for 10 years now).

Even in my case (a tour reservation) object, this issue of serialization ALWAYS comes back to the user interface. (even if it is a simple box telling the user that there are no rooms left in the hotel due to a transaction failure that caused the room count to drop below zero).

You also do not have to assume that the above example of “locking” prevents users from accessing the data. It does not! (that locking example only prevents a process from updating the data..not others from reading it!!! Those process MUST be serailied!)

Thus, the issue of ACID  in no way address or helps as what you will do when two users try and edit the same word document, or a Excel document or CityDesk document (as in this case)…does it?

How does a ACID approach solve the the common problem of two users trying to edit the same word doc?

That is the real issue here…not a lack of a transaction based data engine. (since we assume that is a given!!)

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Saturday, January 4, 2003

"In general, we do NOT have the technology to update partial parts of a word document. "

Albert, don't you mean to say:

"In particular, we do NOT have the technology to update partial parts of a MS Word document. "

There's lots of programs that support multiple writers on ASCII development  documents like .c .cpp .h .txt; I assume that the same could be done for .html documents like Joel uses and could also be done for any proprietary format that the developer wants to provide multiple-writer support for.

X. J. Scott
Saturday, January 4, 2003

Albert, that really is a piss-poor solution.  You might not ever encounter it, but your code is buggy.  There is nothing to prevent another thread from entering that section of code BEFORE you are able to claim the lock.  To ensure that you do indeed own that section of code, you need to ask the operating system for it.  Use something along the lines of EnterCriticalSection().

Hope that helps.

Saturday, January 4, 2003

harp - did you actually read his post? It is about database record locking across multiple users, it has nothing to do with thread synchronization.

Saturday, January 4, 2003


You are supposed to be smart enough to extrapolate.  Create a mutex instead of a critical section if you need to work across process boundaries.  Encapsulate the locking code within whatever synchronization methodology is required. 

Hope this helps.

Saturday, January 4, 2003

While my example is in fact for database a lock, there is absolute no difference if it was a request for a thread/process lock either.

How do you know what the code looks like that executes that lock?

How can anyone possibly make the claim it is possible for two users to execute the code?

How do you know what that lock code does?

The only thing I have exposed in my example is a method of the tour object called “MyLock” How do you know what the code for MyLock does? I  only showed the methoed "MyLock"


You can only guess what that code does for the method MyLock?

However, I am not sure why it is poor? I am always open to suggestions. (I leave a few more obvous details out untill this one clears!).

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Saturday, January 4, 2003

I actually wasn't attacking your solution, BTW.  I just wanted to point out that locking was the tip of a much larger iceberg.

For instance, if you lock in one application, there is nothing to prevent changes from being made from another application.  Same with threading etc.

I think Joel's solution may be assuming a single users.  It is going against access, which tends to be used for lower end solutions.  I doubt he is using global data access objects that can be hit from multiple threads because that is too much writing to do across all these things. More than likely he's done the equivalent of a select for update.

Just a couple thoughts.

Adam Young
Saturday, January 4, 2003

>>Albert, that really is a piss-poor solution. You might not ever encounter it,
>> Use something along the lines of EnterCriticalSection().

So, what exactly is suppose to happen if EnterCtricial cannot in fact be entered? Hum? What do I do? I certainly don’t want to freeze up the user?

On the other hand, how do you know that the code behind my method “MyLock” does not in fact call a EnterCritical routine and ask for a lock? Hum? You don’t?

Further, the way my code is setup it is NOT possible for two users to get lock in that example code anyway!. Again, it is moot if we are talking about a record lock, or a process lock. They really are the same thing in the sense that we only allow one user to get that lock, or run that example process.

The problem always remains the same: What do you do with the user can’t get that lock, or as in your suggestion one CANNOT Enter “EnterCritical”?

Hum, what do you do? You have to tell me?

Fact is, we are actually dealing with JET, and that means we are talking about a multi-user system in a file share mode. That means we don’t in fact have a server to manage our program threads…do we now?

One of my clients is using that tour software has 6 users in the system all day long. Now, how do I deal the problem of two users trying to grab the last hotel room? (and also NOT having the user get stuck in a dead lock?).

By the way the code in “MyLock” does in fact deal with this problem in a very nice way. What would you do? Because really at the end of the day, there is no difference between a user not being able to run your “EnterCritical” and a user trying to run MyLock..

Well, the difference is that I came up with a workable solution…and most people using a file share have not…

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Saturday, January 4, 2003


I understood you to mean that you were using

Boolean MyObject.MyLock()

which was something like:

Boolean CriticalObject::MyLock()
  if (true == this->locked_)
    return false;
    this->locked_ = true;
  return true;

Correct? I assume the detractor did not think you were invoking OS suport for atomic operations within your MyLock(), hence his misunderstanding.

X. J. Scott
Saturday, January 4, 2003

Oops, exit the critical section before returning in the first case of the if clause.

X. J. Scott
Saturday, January 4, 2003

Albert, first you said it was a boolean:

"MyObject.MyLock ... The above is a Boolean, and thus"

Then it looks like you use it as a boolean:

"If MyObject.MyLock = true then"

Now you are saying it is a method:
"I  only showed the methoed 'MyLock'... MyTour.MyLock"

It being a method would make a slight difference, but only if you wrote some really bad code in that method that accessed some globals.  You would still have to use the OS calls to be bug free though.

Why do I know this?  And how can I possibly make the claim that it is possible for two users to execute the code?

Simple.  I know what I'm talking about more than roughly 50% of the time.  And I've been doing multithreaded windows since it was possible to do multithreaded windows.  And I know you develop for windows.  And I've made something similar to that mistake before.

Whether it's a variable or a method that touches a variable, there's no way possible to guarantee that another section of code doesn't alter that variable (or the critical code in the method) after the other section checks on it, but before it can claim the ownership.

Why?  Because you have no control over how the OS grants time slices. 

Take this example:

1:  If obj.notlocked
2:    obj.lock

If you have thread1 and thread2 running through this section of code, there is no gaurantee that just because thread1 got past line 1 successfully that thread2 _the next time slice_ won't make it past line 1 AND line 2.  Boom.  Deadlock.

Sunday, January 5, 2003

Wow XJ, you really took some liberties in assuming what Albert's code did.  Are you guys buddies or something?

You've also listed a dangerous section of code.  You give a naive user of that class the impression that it's thread safe.

1:  Boolean CriticalObject::MyLock()
2:  {
3:    mutex_toolkit::EnterCriticalSection();
4:    if (true == this->locked_)
5:      return false;
6:    else
7:      this->locked_ = true;
8:    mutex_toolkit::ExitCriticalSection();
9:    return true;
0:  }

2 threads can be queued up on line 4.  thread1 executes line 4 and it evaluates false.  Then thread2 executes line 4 and guess what?  It's still false!

Sunday, January 5, 2003

Albert, this is really basic stuff. 

Have you heard of middleware?

Sunday, January 5, 2003

XJ, you caught me on one of my times when I wasn't operating above 50%. 

Of course mutex_toolkit::EnterCriticalSection() wouldn't allow 2 threads to be in it. 

Sunday, January 5, 2003

Ah good, thanks - saved me from replying. The whole point of the toolkit routine is to ensure that task switching can not occur on a uniprocessor probably by disabling interrupts, or do something more complex likely involving a hardware atomic test and set instruction on shared memory if multiprocessor.

X. J. Scott
Sunday, January 5, 2003

"you really took some liberties in assuming what Albert's code did."

Perhaps. I think that I just assume that if someone is using the word 'lock' that they know what they are doing and the issues involved. I sort of think this is a safe assumption since a naive programmer would be unlikely to be using the word 'lock'. I didn't assume that the code Albert tossed out repersented the actual code but was rather a quick and vague approximation, to be easily filled in by persons familiar with deadlocking and spaghetti eating professors and all the usual suspects.

X. J. Scott
Sunday, January 5, 2003

One subtlety to be mentioned is that even if Albert didn't do it right, he's working with a small number of people checking out files in his applications. Overlapping write checkout requests are probably quite rare and the user requests coming in so as to interlace in the right way has probably never happened. If he starts to have more users simultaneously it will be more of a problem.

Where I run into these problems is with multiple threads of interacting audio and midi streams where this stuff kicks you in the head instantly and painfully.

X. J. Scott
Sunday, January 5, 2003

Well, I did state that MyLock is a boolean.

I probably should have ALSO stated that MyLock is a Method of my object that returns  a Boolean value!! (it is in fact a method of the tour object that returns a value.)

Thus, it is some what understandably that I gave the impression that the MyLock is simply a “simple” value (or property) of the object.

(and I do allow multi instances of that object by the way).

It is a method of the tour object. In addition, it really can be considered a “mutex” for all intensive purposes.

It actually tries to do a insert to into a table and looks for a key violation (that key based on the workstation name and a id). So, it is in effect my own mutex (MUtual EXclusion object). In fact, people have used their database system since the dawn of databases as a mutex server!

However, this really begs the issue. Even if it is a simple Boolean flag, how in world can you possblity make any assumptions about how the Boolean value got set? It is really is a waste of my time to sit here and justify or even explain that fact that MyLock is a method, or just a property that returns a value. I could sit here and say I am lucky since it is a method that invokes code! However, since none of code showed how that lock value got set I am stumbling to understand why your argument of the fact that it is a simple Boolean value, or a method makes a hill of beans differance? The fact of it being a simple Boolean, or a complex method makes NO DIFFEREANCE!!! Why would it make a difference?

I am trying very hard to understand why you are making such wild assumptions about my code? So far we have a assumption that a value is a Boolean and not a method (which, I fail to understand why it makes a difference?). At least you mentioned that method would make a “slight” difference! (it is not big deal!).

To qutoe:

>>> It being a method would make a slight difference, but only if you wrote some really bad code in that method that accessed some globals. You would still have to use the OS calls to be bug free though.

Why do you make the assumption that I have to make a OS call? Why do I have to use the OS calls? You have made zero case here? Do please tell my WHY I have to make a OS call to be bug free? In other words, if I DO NOT make a os call, then I have bugs? Wow!! Why and how can you possibly make such a sweeping statement here?  Also, why bring the issue of globules? For what reason do you then need to bring in the issue of globals? For launch another attack on me?

Please explain why you would bring up the issue of globals? The person can launch two copies of my software on the same workstation…and it all works fine. In addition, multiple instances of the object often exists. However, again, why bring up the issue me using global variables? (I don’t’, and I can’t anyway). Do you actually think that someone would try and use globals for a process lock? Gee, how stupid do you think the rest of the world is compared to you? It is not even a question of you accusing me of using globals, it is fact of bringing it up, and then stating the ONLY way to be BUG FREE is to use a OS call!  In other words, don’t use glboals..and you have to use a OS call (which by the way if funny, since I use neither!!).

I am missing something here?  I am really a bit confused here!

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Sunday, January 5, 2003

Albert, I've already explained all of this above.  Which part are you having trouble understanding?  Maybe you should give me an example of code that sets a boolean as a lock variable that isn't bug free and doesn't make an OS call.

Sunday, January 5, 2003

Oops, _IS_ bug free.

Sunday, January 5, 2003

Well, the problem I am having here is that I am being told that I must make a call to the OS for this process lock? Why?

Further, you do realize that with JET we have no central server here. In other words, each user is running their own copy of the code on each pc. There is no server side code running on the server!. In fact, this means that NO code is run on the server, it is ALL on each client computer!

However, that above again is really is a moot point. As mentioned, people have been using their database system to create process locks since the dawn of databases that offered record and RI abilities.

I see no reason as to why I can’t use the database system in place of a OS call? Why?

If you are asking me how JET prevents two people on a network from adding the same key to a table? Well, I guess we would have to delve into the details as to how JET manages a record lock on a network file share. However, I don’t have wait, or figure out how that locking mechanism work, all I have to do is check for a key violation. Either JET allows, or does not allow me to add that key value!! It is one or the other..but I don’t site there waiting). The end result and effect is no different from a process lock.

Beyond the above information, anything more is simply an abstraction (ie: I don’t know how EnterCriticalSection() gets its lock, or how JET does. All we both know is that they work and that JET does NOT allow other processes to insert a key that is already in a table.!!). What is the difference?

So, my process creates lock by inserting a key value into a table. If that key value is already there..then we get a key volition, and the routine thus does not get a lock! There is certainly no OS call! (of couse..there is no code on the server either…but again that makes no difference).

It really is not important, but the code that attempts the lock (lock method of the Tour object eventually calls a general lock routine of mine. The  code that we eventually get to is:

  tblLockRecords!lockitem = strKeyLoc
  tblLockRecords!NetWorklogon = UserLogonName
  tblLockRecords!ComputerName = UserMachineName
  mylock = True  'lock is succesful

Exit Function
  ' this error assumes that the key was already in the table, and thus the lock
  ' failed
  mylock = False
  tblLockRecords.Requery    ' we are still in a edit state...flush/release index lock(s)

However, again, I fail to see why the above code makes any difference!.

You have not made any case as to why I must make a os call? The above concepts apply 100% to if I was using a server based system.

People right now are using MySql on Linux with the inno extensions to act as a Mutex server.

Why must I use a OS call here again begs the question?

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Sunday, January 5, 2003

JET is doing the locking for you.  It is making the OS call.  How do you think it prevents 2 simultaneous updates from succeeding?

Sunday, January 5, 2003

Albert, JET handles locking for you.

It looks like you developed your own scheme though.  Correct?  If so, why?

Bob Greene
Sunday, January 5, 2003

Well duh?

I mean that is like you claming that I must use assembler to add 2 +2 = 4

I don’t use assembler to add 2 + 2 = 4, but I am sure assembler eventually comes into play.

Lets not deal with such a absurd level of semantics here!!

Surely you are not grasping at such low straws?

I make no calls to the os!

Heck, we could be talking about 15 computers in a server based farm, and all 15 of them are connected to a Oracle database..

You are now going to come into the room and suggest to the developers that that 15 computers have  to make a os call for a lock? How ridiculous can you get? Further, what really insults me is that you used such a lame argument to state that it is s piss poor solution! That is really low of you!

Stating that a computer eventually has to come up with a process lock is fine. I can agree with your statement.

However, to blast me publicly with such a lame argument for my approach is where you stooped low. I don’t mind people criticizing me  at all. But at least have some foundation to stand on here!

Fact is, those 15 computers don’t have to call the OS for a process lock (in fact, it would be stupid if I walked into the room and told the developers they have to!).  The standard approach is that the 15 computers requesting the same product, only one will be granted the key value. The rest will fail, and thus they will not be able to run the purchase process code. To say that we solve this problem with a call to the OS has got to be a grasps at some straws. Heck..the straws are so thin I can’t even see them! What a stupid idea!

My gosh surely  you are not going to stand behind such a lame argument as that? Using the above lame argument to call my approach buggy, and piss poor is not much ride on is it?

Gee, I can just see you suggesting to the Oracle developer that you “MUST USE A OS CALL, or else to quote you: “is piss poor approach , or buggy?”

Come on here? Really, is that you only answer? That eventually the database system has to do a call to the os for lock?

That is like telling me that I have to use a OS call to add 2 + 2.

Well, duh!!!…thanks for enlightening us all in this group with the fact that a computer has to run code to accomplish a task!

Gee, thanks for that big new piece of info!!

To be fair, you were suggesting to make a OS call, and I was most surprised.  My code DOES NOT, and DOES NOT HAVE TO make a os call. (sure something down the line might..but that is a silly conjecture)

If that is your only argument here, then I have to wonder why my approach is piss poor and buggy?

You sure don’t have much to stand on here…do you?

Look, at the end of the day, you were assuming that we were taking about a process or thread running on a server. You can simply state that you did not think too much before you make a response!

In fact, in most distributed environments, you will wind up using the database server to control these process.

I fairly accept your mistaken assumption. However, don’t just sit there and fall over. Simply state, yea… I popped off my top a bit..and should not have stated that my approach is piss poor and buggy.

No big deal..I am not looking for a fight here…just a reasoned approach to problem solving. I sincerely believe that you also believe that we are all here to learn more.

This is public form..and we all snap a bit.

I walk from this one with no bruises or ill feelings.

In the scope of things…this is not too important!

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Sunday, January 5, 2003

>>Albert, JET handles locking for you.

It looks like you developed your own scheme though.  Correct?  If so, why?

Good question!

I wanted something for controlling processing in addition to record locking. In other words, not only did I need to control some record conflicts, but I also wanted something to control some types of processing. Thus, I need a mechanism for this. Thus, in fact I am really am "rideing" off of the locking stuff built into the JET system. In other words, I simply try writing out a key value to a table. Either I get the primary key, or I get a error from JET that someone else did!!. Hence, it is really JET that does the dirty work for me and figures out who actually gets the key. Thus, I am using JET as a Mutex (or a Process) server in this case!

Also, remember that transactions in JET only allow a transaction to be rolled back, but do not prevent two process from running! (big difference between that). (and course transactions are separate issue from table locking anyway).

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Sunday, January 5, 2003

My apologies Albert.  I am used to dealing with low level internal details.  I sometimes forget that this forum is inhabited by a lot of VB developers.

Sunday, January 5, 2003

Thank you kindly harp.

I will say that I am also a bit to blame in running this thread down this path also.

I probably should not barked about “why” I can’t lock, and should have really just came out and stated that I am using a database, and how that lock works.

It sounds like you think in terms of server based software, or simply in terms of threads (something us VB folks don’t worry about!!).

Regardless, the fact that I did have to explain how I accomplish a process lock using a database turns out that this thread does/did have some learning value!

Anyway, thank you kindly.


Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Sunday, January 5, 2003

So how is it that CityDesk would benefit from rolling-your-own locking rather than using the built-in locking offered by Jet?

Herbert Sitz
Sunday, January 5, 2003

If I might interpret: Albert, you were asking (amongst other things) Joel how he deals with the processing (UI and/or business) for the case where someone requests a piece of data that has been locked by someone else.

In the web, you tend to want Transactions processed completely between web page requests.  The main reson is that otherwise you will lock any resources in early requests until the transaction completes or rolls back.  Since we are dealing with the web, this is  possibly going to be when the session times out.  If your session time out is on the nature of 10 minutes (not unusual) you will have lots of locked resources.

So with the "Reserve the last hotel room" problem you spoke about before, you are going to have a race condition.  Who ever gets there first wins.  You may see in one vioew that the room is avaialbe.  Next view it may not be.  You have to check at the Reserve event/request whether or not it is still available.

For the Content Management issue, it is more significant if two people are updating a particular piece of content at the same time, as there is no good way to merge changes, and the web does not allow you to save locally.  At least not easily.

I think in this case, you don't just want to depend on the DB, but want an state field on the row in question, that gets updated.  This can help out workflow, since instead of just locked and unlocked you can have new, locked for editing (with locked byu another field in the table), approved, posted, removed and what ever other states you want.  Yes, you have to be sure to unlock at session time out, but you can also allow a senior member of the team to grab the lock : John has an emergency and will be gone for two weeks.  Everything he has been working on gets reassigned to a co-worker by hi boss.  You get the idea.

BTW, something like CVS may be a better tool for the content storage facility than a DB.  You can have an article ID stored in the DB, and the actual article stored in the file system, with a tie in to a revision control system.

Again, just a couple thoughts.

Adam Young
Sunday, January 5, 2003

My understanding was that even though CityDesk is a CMS system for the web, all the editing is done locally and the changes/additions are just periodically automatically ftp'd up to your website.  In other words, there is no database on the web, the database is local to the CityDesk installation.  The local install may be networked with several different users, but there is no web access to the database.

Given that, I'm not sure why CityDesk can't work just fine with the Jet's default optimistic locking.  I've designed lots of networked databases and never had to manage my own locking; I can't see why CityDesk would need to be different.

Herbert Sitz
Monday, January 6, 2003

Reading this thread, I think I understand where the phrase "to harp on" comes from


Gwyn Carwardine
Monday, January 6, 2003

>>the web, the database is local to the CityDesk installation.  The local install may be networked with several different users, but there is no web access to the database.

Given that, I'm not sure why CityDesk can't work just fine with the Jet's default optimistic locking.  I've designed lots of networked databases and never had to manage my own locking; I can't see why CityDesk would need to be different.

CityDesk is not much different then just editing a record. Thus, you can use JET locking. However writing an article can take quite a bit of time. (in fact, a lot of time!) Hence, some effort as to how to deal with this problem might be worth it. In addition, Joel’s post was about abstracting the database one level down from the application. So, rolling ones own locking or “contention” scheme certainly might be in order here. (you don’t want to tell a user after spending an hour writing a article in CityDesk that you can’t save it because some other person on the system went in and corrected the spelling of ONE WORD!).

Thus, a decision has to be made as to what type of locking (and more importantly the GUI to manage this). Do you let two users view the same document at the same time? Who gets the edit?

Do you use Optimistic locking, or  pessimistic?. Or better..roll you own?

The real question will eventually become how you handle this process from a GUI point of view, and not really a bits and bytes and a special database code issue (since one can pretty well do what you want anyway).

When you click on a link in the browser, a little globe in the corner starts. If you click stop…you can bail out.  This approach by the way works VERY well for a lot of GUI tasks. Is fabulous way to get/let users out of a deadlock situation.

If the resource does not become available. it record locking, or other asynchronous tasks, then the user should be able to bail. In fact, this is exactly what my locking strategy allows and why I rolled my own.

Even if the new Version of City desk works with SQL server, the issue of the GUI and management of conflicts comes into play. I was curious if any strategy was applied in CityDeskto this common UI problem.

Albert D. Kallal
Edmonton, Alberta Canada

Albert D. Kallal
Tuesday, January 7, 2003

*  Recent Topics

*  Fog Creek Home