Autore Topic: Blocco file con comando LOCK  (Letto 1071 volte)

Offline g.grandi

  • Gambero
  • **
  • Post: 51
    • Mostra profilo
Blocco file con comando LOCK
« il: 09 Settembre 2013, 12:33:29 »
Salve a tutti. Come al solito mi imbatto in misteriosi comportamenti di Gambas. Volevo provare il comando LOCK per bloccare un file in modo esclusivo ed ho semplicemente provato a scrivere l'esempio che riporta nella guida in linea del comando stesso. Il risultato che ho ottenuto appena clicckato sull'esecuzione del programma è "Useless LOCK  alla linea....". A tutt'ora non c'è stato verso di fargli digerire questo comando e il messaggio non mi aiuta certo a capire cosa vi sia di sbagliato. Qualcuno è in grado di darmi aiuto?  Vi scrivo di seguito la semplice sub che ho usato per la prova.

Ciao, Giovanni

PUBLIC hFile AS File

PUBLIC SUB BloccaFile()

  hFile = OPEN "miofile" FOR READ
  LOCK #hFile

END SUB

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.727
  • Ne mors quidem nos iunget
    • Mostra profilo
Re: Blocco file con comando LOCK
« Risposta #1 il: 09 Settembre 2013, 12:58:42 »
... mi imbatto in misteriosi comportamenti di Gambas.
      ;D

Anch'io ho avuto qualche esperienza misteriosa con il comando Lock...
« Ultima modifica: 10 Settembre 2013, 09:04:55 da vuott »
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »

Offline g.grandi

  • Gambero
  • **
  • Post: 51
    • Mostra profilo
Re: Blocco file con comando LOCK
« Risposta #2 il: 09 Settembre 2013, 15:42:50 »

Ho provato ad usare il comando come tu descrivi sul Wiki ma combina comunque casini. Io in pratica, devo bloccare un file esistente mentre lo modifico ed impedire ad altri processi di fare lo stesso, fino a quando ho finito. Provando con il comando LOCK, mi sparisce l'intero contenuto del file! Penso proprio di rinunciare a questo comando e ripiegare per un'altra soluzione.

Ciao, Giovanni

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.727
  • Ne mors quidem nos iunget
    • Mostra profilo
Re: Blocco file con comando LOCK
« Risposta #3 il: 09 Settembre 2013, 20:11:51 »
Dalla Mailing List ufficiale ti riporto una discussione, apparsa nel 2008, molto simile a quella da te posta:


" I am writing an application in gambas (2.7.0-2.1) on opensuse 10.3 -
11.0 and I need to lock a file when I open it.

My code:

Codice: gambas [Seleziona]
...
hfile = OPEN "/usr/far/alman/NOMEFARM.DAT" FOR INPUT OUTPUT
TRY LOCK hfile
IF ERROR then
    ...
ENDIF
LINE INPUT #hfile, MyVar
UNLOCK hfile
CLOSE #hfile
...

When I execute it I get the following message:
Useless LOCK in line ...

Why ?

Regards,
Massimo.
"


" # If the READ or WRITE keyword are specified, then the input-output
are not buffered.

# If the INPUT or OUTPUT keyword are specified, then the input-output
are buffered.

Maybe for that ?

Fabien Bodard
"


"No, because if I change the line where I open the file:
Codice: gambas [Seleziona]
hfile = OPEN sNameFile FOR READ WRITE

I obtain the same message.

Massimo
"


" Can you tell why you have to lock it..
for what reason?

nando
"


" I need to lock the file to avoid writing by another user in multiuser
environment

Massimo
"


" Sorry for that, the documentation is completely false. But who wrote it? :-)

The LOCK instruction does not lock a specific stream not a stream, but instead
use a specific path to create a global system lock.

You use it this way:


Codice: gambas [Seleziona]
DIM hFile AS File

TRY hFile = LOCK "~/lock"
IF ERROR THEN
  PRINT "Already locked by something else!"
ELSE
  PRINT "Got locked!"
  ...
  UNLOCK hFile
ENDIF


Do not rely on the lock file contents. It is truncated to zero byte when the
lock is acquired
.

Regards,

--
Benoit Minisini
"


" In some applications, I create a folder as a lock.
The existance of the folder indicates 'locked'
It works very well and is used through Samba to share
the lock on both the linux and windows side.
-Fernando
"


" For my apps in Gambas, I use lockfiles, not folders, to lock a certain
file. Experience told me that "sometimes" when two users/processes want
to access the same file, these may overlap. So I added an identification
method and a time delay in case of locks. This is what it looks like
(kinda prototyped):

Codice: gambas [Seleziona]
WHILE Exist(theLockFile) THEN

   OPEN theLockFile FOR READ ...
     LINE INPUT firstLine
   CLOSE ...

   IF firstLine = myOwnName THEN 'old lock file from myself:
     KILL theLockFile              'may be deleted, then proceed

   ELSE                          'lock file from someone else:
     WAIT one second

WEND

'now first of all, write my own lockfile
'then begin reading the file I want
'finally delete lockfile

This is how this looks in practice:
Codice: gambas [Seleziona]
WHILE Exist(pfad &/ "klassenbuch.block")
     OPEN pfad &/ "klassenbuch.block" FOR READ AS #dtnr
       LINE INPUT #dtnr, t$
     CLOSE #dtnr
     IF t$ = system.User THEN
       KILL pfad &/ "klassenbuch.block"
     ELSE
       WAIT 1
     END IF
   WEND


   OPEN pfad &/ "klassenbuch.block" FOR WRITE CREATE DIRECT AS #dtnr
     PRINT #dtnr, system.User
   CLOSE #dtnr

   OPEN karteiVerz &/ jg &/ "klassenbuch" FOR READ AS #dtnr

nd so on. While I am writing this email, I find that this has one big
drawback: If the lock file was made by someone else and that app
crashed, my program will wait forever for the lockfile to disappear. So
one should add a counter, e. g. 5 seconds, to stop waiting.

Regards

Rolf
"


" So, use the LOCK instruction to create a system-wide mutually exclusive code
section!

--
Benoit Minisini
"


" There is a problem with your method.
Between the execution of the KILL and the CREATION of the file,
multitasking happens and another task could create the file just before
the original thread executed the CREATE.


In different words, if the first thread lost the CPU just after the KILL command
due to multitasking and a second thread began the WHILE condition,
the WHILE would not execute because the WHILE condition fails and
then the file is CREATED with the user of the second thread.
Then, multitasking happens and the second thread loses the CPU and the first
thread continues and it will then create the file destroying the second
threads information.  Anything could happen in a multitasking environment.

I use directories because folder creation is a one-step process there isn't
time between
the two steps for
another task to steal the lock in betweem code statements.
An example is something like...

Codice: gambas [Seleziona]
'create lock, this can be in a SUB with tje folder name passed in

counter=100     '100 loops of 0.1 sec = 10 seconds max to try lock
flag = FALSE    
WHILE flag = FALSE
  TRY MKDIR "lock-folder-name"
  IF ERROR THEN  'it failed
    flag=TRUE
    error.clear
    WAIT 0.1
    dec counter
    if counter<=0 then
      'you could say the the lock took too long
      'you could allow a user choice to retry or blindly continue.
      break   'we will assume something is locked too long
    endif
  else
    flag=FALSE
  endif
wend


'unlock, this can be a SUB too
TRY RMDKR "lock-folder-name"
error.clear

You don't need to know who has the lock because only a successful lock
leads to code writing some important information for the thread that
successfully
locked.  The unlock must follow very shortly thereafter to release the lock
and cannot hog the lock.

-Fernando
"


" r better method: use the LOCK / UNLOCK instruction!

--
Benoit Minisini
"


" Yes Benoit, we know, but this is hobby :-) just having fun with an own
algorithm...


But I've got a question about LOCK. I didn't know it existed. A long
time ago when I started with Linux, I learned that Linux doesn't have a
file locking mechanism. Isn't that true anymore? Never heard or read
about it in all these years.

After all, a lot of programs still use lock files, and for instance
there is a discussion about Firefox 3 not running correctly on terminal
servers because it's got a new file locking method which blocks more
than 1 users.

Having a first glance at the description of LOCK, there is one thing
which leaves at least a small pain in the neck: You have to open the
stream before being able to try a lock. That leaves the impression that
without checking the lock, you can read/write the file anyway. Two
questions:

Is this LOCK just a flag? (Which I can obey or not just like I feel) Or
does it really prevent any other access (or at least writing into the file)?

And what happens if my program "forgets" Unlocking it? Will the file
stay locked (file system wide - until reboot) or will the Lock end
automagically when I close the file or when the process ends that opened
the stream?

(Maybe it would be nice to insert such information into the help, too)

Regards

Rolf
"


" The documentation you read is false and out of date. Look at the updated
documentation in the wiki.

I cannot say more than what I already wrote there:

LOCK uses a file to create a system-wide global lock. Without using a file, it
couldn't be system-wide.

It actually uses the lockf() POSIX file lock routine to lock the file so that
only one process at a time can acquire the lock.

The contents of the underlying file is not important. But do not write in it,
as acquiring the lock automatically voids the file!

LOCK returns a Stream object that you use to UNLOCK the file.

And halting the process automatically releases the lock - Important feature!

I hope I have shown that LOCK is useful :-)

Regards,

--
Benoit Minisini
"


" Thank you Benoit for your answer! Now I tried around with it a bit and
tested the example from the Wiki, and there are some questions left:

> The documentation you read is false and out of date. Look at the updated
> documentation in the wiki.
>
> I cannot say more than what I already wrote there:
>
> LOCK uses a file to create a system-wide global lock. Without using a file,
> it couldn't be system-wide.


Alright, but it doesn't delete the lock-file with UNLOCK, and the
lock-file has standard rights rw-r--r-- (644), so no other user than the
one who has created it should be able to delete it. Up to now, I just
tested it on my own account with a file from my own, but I would be
surprised if this mechanism made an exception from such basic rules here.

Codice: gambas [Seleziona]
DIM hLock AS Stream
' Try to acquire the lock
TRY hLock = LOCK "~/my-lock"
IF ERROR THEN
   PRINT "Locked is already acquired. Try again later."
   RETURN
ENDIF
' File is locked, you can do the job now!
...
' Do not forget to release the lock
UNLOCK hLock

The most important question here is: What file does this lock-file lock
(or: what file does it refer to)? But then:

> It actually uses the lockf() POSIX file lock routine to lock the file so that
> only one process at a time can acquire the lock.


Browsing through the descriptions of lockf() and fcntl() in the internet
I found there are two different ways of implementation: mandatory and
advisory. From my first tests here I assume that you chose advisory, is
that correct? This would mean, your implementation doesn't refer to a
specific file but can be just set or unset as it is used from time to
time by the specific Gambas application, but it doesn't have any effect
on other apps accessing the locked file. Am I right here?

> The contents of the underlying file is not important. But do not write in it,
> as acquiring the lock automatically voids the file!
>
> LOCK returns a Stream object that you use to UNLOCK the file.
>
> And halting the process automatically releases the lock - Important feature!


But the lock-file doesn't disappear nor are its rights changed - so how is it unlocked?

Regards

Rolf
"


" Be aware - to delete a file you don't need permission to write to it;
you need permission to write the directory where the file resides...

cheers,
Doriano
"


" Of course, that would mean, the next process of another
user could delete it and set a lock-file with this user's rights.

I tend to forget this...

Thank you

Rolf
"
« Ultima modifica: 10 Settembre 2013, 01:57:58 da vuott »
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.727
  • Ne mors quidem nos iunget
    • Mostra profilo
Re: Blocco file con comando LOCK
« Risposta #4 il: 09 Settembre 2013, 20:17:36 »
Un'altra del 2009:


" Once upon a time, maybe some weeks or even months ago, we had a
discussion about how to lock a file for other users while it is written.

My usual way was (and still is) using a xxx.lock file for indication.

Someone on the list pointed me to the danger of overlapping
file-creation and directory-reading so that in rare cases there would be
no locking effect. He used locking by creating a lock-directory. After
some 6 months in use, I have to admit that he was right. Every 6 or 8
weeks or so, there is a collision with my system.

I can't find these posts in the archives anymore, so please could you
(whoever it was :-) ) give me your idea of locking by directory again?

Thanks a lot in advance!

Rolf
"


" Sharing a file exclusively locked very briefly and getting everything
to work correctly amongst multiple programs can be brain-twisting.
With increasing use of mulit-cores, the possibility for sharing files
using any lock technique is more important than ever before
because all associated programs can execute simultaneously.

nando
"


" You can use the LOCK/UNLOCK commands and a specific void file on the disk to
create system exclusive sections in your code.

Let's suppose you want to modify a file named "/tmp/foo".

Codice: gambas [Seleziona]
...
DIM hLock AS Stream
DIM hFile AS Stream

' Try to acquire the lock
TRY hLock = LOCK "/tmp/foo.lock"
IF ERROR THEN
  PRINT "'foo' is being modified. Try again later."
  RETURN
ENDIF

' Lock was acquired, you can modify the file now!
hFile = OPEN "/tmp/foo" FOR OUTPUT
...
CLOSE #hFile

' Do not forget to release the lock
UNLOCK hLock
...

--
Benoît
"


" Just one question:

Why do you use AS Stream and not AS File?

Rolf
"


" It is a mistake. You can use Stream for the lock, as you just use hLock with LOCK and UNLOCK.
But hFile should be a File.

Regards,
--
Benoît
"


" IMHO "the one" drawback remains: foo.lock isn't foo. You cannot lock the
file itself, relying on the operating system / the file system to really
make sure nobody else is able to corrupt your file during your access.

If there's some other app that doesn't care about .lock files, your
/tmp/foo will be lost. Of course, the same applies to my own methods
like lock directories or self-made lock files.


The only way to solve this would be a mechanism in the file system
itself, giving the opportunity to code something like

Codice: gambas [Seleziona]
hFile = OPEN "/tmp/foo" FOR OUTPUT LOCK


which would unlock the file automatically when you CLOSE it (or the
instance of your application ends).

Regards

Rolf
"


" I think you're looking for the filesystem to prevent you from file operations
while 'locked'.  I used 'locking' with VB5 and relied on Window's internal locking
mechanism. After years of agony, I found the external subdirectory method
the best.  I don't have shared files on just one computer, there are many computers.
I found that VB5 sometimes got stupid and never unlocked - especially when
long time between reboots.  But now, with the subdir method, I can fully control it.
I do this with Gambas using Samba while I port away from VB5.

The main problem is this:
Program 1 locks a file (or a portion of a file) and that program freezes up,
locks up, computer is shutdown, etc WHILE the lock is in force.
Then other programs try the same and....
The whole system is screwed!
Now that I use subdirs, I know that a lock 'should be' free (ie unlocked)
within 10 seconds because I know that there is no way my code should last
that long....and something is wrong.  In such a case, I can actually
proceed (fake re-locking it) with the second program, give a warning message
that it couldn't lock because it is taking too long but is proceeding
and will subsequently unlock (rmdir) too.
In very few cases, I have had to go in and RMDIR manually because things
got jammed up, but discovered those situations to be wierd-condition bugs
which are now fixed.
Personally, I like the full control it provides and works over multiple
programs and multiple computers.

nando
"


" That's right. I tried your idea, and it works fine as long as every
other program keeps looking for the dirs.

My problem is similar: Part of the software runs on Windows, originally
programmed with VB5. To make it compatible to the Linux side client, I
introduced a lock-file scheme. It still uses that successfully.
Unfortunately, I lost the VB source over the years :-( so it has to keep
on running like that, and so has the Linux client. Luckily, though, we
are only 2 persons in our offices who use this thing on a daily basis,
so it's no harm. There are some other Linux programs meanwhile using the
data, but do not write to it, so no real danger.

But for my next-generation tool for the teachers there was trouble with
my lock-file system, so I changed writing/reading to your lock-directory
system. Easy thing, and up to now (a week or so...) it seems to be
running well. I cannot use Benoits LOCK Stream here because this program
is still Gambas1.

I had your idea of waiting, but writing "nevertheless" and just give a
warning as you do is even better. Maybe I'll change that part, but I'd
prefer keeping the original and writing into temporary files until Admin
cares about it... :-) As this application has a messaging system, I
could even send me a message in this case!

Regards

Rolf
"


" I add that to my TODO file.

Regards,

--
Benoît Minisini
"





Ed un'altra discussione del 2010:


" Just tried to rewrite a function I used for locking files in my Gambas1
apps. It was based on making a directory for locking, and it delivered a
Boolean about if locking was successful or not. Think of it this way:

Private Function FileIsLocked(filename As String) As Boolean

Now, when I tried to change the functional part so it uses LOCK instead
of my original mechanism, I get "Useless LOCK". Does this mean, LOCK can
only be used within an OPEN/CLOSE statement? Or maybe only directly
following an OPEN statement? That would be a shame, because this would
mean doing without my own function and including a lot of code into each
OPEN session anywhere it is needed.

This wouldn't only blow up my code unnecessarily but also require me to
browse through all the places where file locking is used.

Or did I get this error message wrong?

Regards

Rolf
"


" Ok, sorry - I found out the reason for my confusion myself :-) I didn't
have a look into the online documentation and got confused by the old
description given in the installed docs.

Isn't it possible to compile the online docs into the distributed ones
automagically?

It seems to run now, though I still have one question left: Is it
correct to "open" the file twice, once for LOCK and once for OPEN? In
other words: The LOCK statement produces its own stream for the lock
file, this is what I understand from it. Then I will have to open it via
another stream as usual for reading or writing or whatever.

Or how do you use it?

Rolf
"


" Not easily at the moment. In Gambas 3, I use directly the online doc in a
Webkit browser with a data cache.

> Or how do you use it?

No, LOCK must be used alone on its file. The file is just there for creating a
system-global lock. You must not store anything inside this file.

Regards,

--
Benoît Minisini
"


" Thanks for your answer. Is it correct that when I close this stream by
UNLOCK, it isn't deleted and has to be KILLed programmatically? What
will happen to another user if his app looks it up after it has been
UNLOCKed but not deleted?

Regards

Rolf
"


" All locks are automatically unlocked by the system when the program ends. But
the files are not deleted. The other applications just see the file on the
disk, and can use them for locking without any problem.

Regards,

--
Benoît Minisini
"


« Ultima modifica: 10 Settembre 2013, 01:31:00 da vuott »
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »