Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Modifying the same file by multiple users at the same time

Status
Not open for further replies.

sashaDG

Programmer
Jun 20, 2022
112
0
16
BY
Good afternoon, there is a problem. The application is located on a network drive,
users launch the application and select which file to upload to the Grid using the OptionGroup.

After selecting a file, copying took place (if the source was not available successCopied = .F.
(for this try catch) , "old file" was taken as the source
placeTo that was previously created).

Code:
	TRY	
		COPY FILE (placeFrom) TO (placeTo)
		successCopied = .T.
	CATCH 
		successCopied = .F.
	ENDTRY

And everything is fine, until two (at least) users try to simultaneously select
the same file (this means that there are two requests for the same path). The second user gets an error:
Code err 2059. "unhandled structural exceptions" ErrorNo 1705. Message: file access denied. LineNo:70 SSET DOHISTORY TO

I understand that it is necessary to make an alert and copy after a few seconds instead of an error. I tried with try catch,
but having learned that it is impossible to use return, I realized that it would not work.
 
The unhandled sructural excaption only happens, when you CATCH TO an exception object, so either you don't post your actual code or this error comes from another pllace, perhaps from a TRY CATCH within main.prg

The code specified (SSET DOHISTORY TO) also doesnt match this try..catch. This code is fine and will work locally and suppress copy file error. I'd say The problem you have comes from elsewhere, where you also find this strange SSET code in line 70.

At places you do CATCH TO an ecxeption object you assert that you handle the error in some way and don't just read out the errorno and mesage from the exception object. And in that case, if you don't something specific with the exception - I have to look up how this works, you get error 2059 "unhandled structural exception", that then is a follow up error of not handling the original error. Which you actually rarely need to do, in this case it could be of interest what exactly didn''t work out, access to the source file or the destination location can be the problem. Caused by concurrency that's close enough, during the time needed to copy the file, for example. For a 2GB file over a 100 MBit LAN this can be a time window as macroscopic large as 30 minutes.

You could address that problem by first reserving the file so it's not offered to others while in transit. That problem preventive solving is sometimes the best way to deal with it. There stilll can be the concurrency of reserving the file (in whatever way you do that, for example by an RLOCK of a record about that file), but that usually has a much shorter time window of concurrently happening, also RLOCK() will just return .T. or .F. and not cause an exception to handle.


Chriss
 
In main I don't have try catch, also I don't have code with set DOHISTORY TO.

access to the source file or the destination location can be the problem
I'm 80 percent sure it is.

You could address that problem by first reserving the file so it's not offered to others while in transit. That problem preventive solving is sometimes the best way to deal with it. There stilll can be the concurrency of reserving the file (in whatever way you do that, for example by an RLOCK of a record about that file), but that usually has a much shorter time window of concurrently happening, also RLOCK() will just return .T. or .F. and not cause an exception to handle.

I have not come across redundancy, RLOC and concurrency, I will read about it
By the way, where can I read about concurrency?
 
Sasha,

First of all, you quote SSET DOHISTORY TO. Can we assume that is a typing error in your post? That is should read SET DOHISTORY TO - with a single S?

Moving on from there, I assume you found that code by using the MESSAGE() function. If so, then SET DOHISTORY TO must appear somewhere in your code. And it must be at that point that the error is being triggered. You say that you don't have it in your code. Have you used Code References to make certain of that?

If it is that line that's causing the error, it has nothing to do with the multi-user copying of the file. It wouldn't trigger a "File access denied" error, which is what error 1705 is. In fact, I can't see why it would trigger any error. So it's safe to assume that it has nothing to do with the issue.

So let's instead focus on the actual problem, that is, how to prevent two users trying to copy the same file at the same time (or perhaps copying two different files to the same destination at the same time). What you need to do is to apply some form of locking. But unfortunately VFP doesn't provide a way to do that in these cases.

One solution might be to set up a semaphore. This involves using a designated record in a special table - one that is set up specifically for this situation. When a user want to start the copying, they try to lock that record. If the lock succeeds, they go ahead with the copying, and then they unlock the record. If the lock fails, they see a message advising them to wait a few seconds and try again (or, the program could automatically try again after a few seconds without bothering the user).

I don't know if that is the best way to handle this situation, but it is fairly simple and it should work.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I have a feeling you do general error handling with a TRY CATCH in your start/main.prg, that's no good idea.

If you do something like

Code:
goApp = CreateObject("Applicationclass")
TRY
   goApp.run()
   goApp.quit()
   goApp  = .NULL.
   RELEASE goApp
CATCH TO loException
   ...
ENDTRY
Not necessarily exactly that but something similar where you try to catch any error that happens in the course of the whole application anywhere at that central place.

That's technically possible, i.e. if you introduce no other handling but only use TRY CATCH at other places, any error occurring makes VFP jump here and go into the catch branch that may eventually return/continue or just log the error and quit the application, but that introduces problems you just now become aware of.

Structured error handling should only be used for handling local problematic cases not for general error handling, i.e. TRY..CATCH wasn't introduced as successor of ON ERROR, it just introduces the possibility VFP previously lacked to do structured error handling, which was previously limited to the error event of classes or was trickily done by temporarily setting ON ERROR plError = .T. to check at some point whether plError is declared and .T. or not. Or even just suppress errors with ON ERROR *.

I suggest you use code referrences not only to look for SET DOHISTORY but also to look out for CATCH TO usage, maybe you even get to the same place, and SET DOHISTORY is part of the catch block of your "general" TRY..CATCH error handler.

Don't get me wrong, as this technically works, it also has a use case in some way, but it does not make debugging easier.

Chriss
 
On the topic of the problem with file modifications byy multiple users. You mainly read the same file, that's possible shared with multiple users.

But the core error you showed:
Code:
ErrorNo 1705. Message: file access denied
That's file access problems, and you can get that on both the source file and if you copy to the same destination it can also be a problem of writing to the same file. As already suggested, to do do prevention, stop a second user from copying the same source file, or at least stop them from writing to the same destination.

I don't know what you mean by
SashaDG said:
which file to upload to the Grid

Do you mean FTP file uploads? To the cloud? Is it a list of open jobs to do uploads that's shared between multiple users? Then the idea with RLOCKs stays. Also, you always have to think about concurrency not only as something happening at the same split second, that has a low chance. If two users start a form within say half a minut, and the first one filters a list or gets it displayed and looks and thinks about it, make some google search or whatever in parallel, when finally picking a file. The second user, even though starting 30 second later, still also sees the full list, nothing has been processed yet, so he can for examle still pick the same file. At that moment you have another point where you need to check, you should not rely on the form opening moment only, where a user only sees unprocessed files. That truth can change during using the form. Concurrency problems are most often not those low probability same time problems, but such strategic problems.

When you picked a file and that file has a record in some central dbf and you can RLOCK it, you can prove that no other user has currently picked that file and prevent working on it in parallel.


Chriss
 
Let me also ask a question: Would you like two users to be able to copy the same file? And does each one have a different target directory?
It sounded to me that there is a backlog of jobs to be done and no matter who does it, it's only to be done once. But maybe my impression about that is wrong.

Chriss
 
Mike Lewis
First of all, you quote SSET DOHISTORY TO. Can we assume that is a typing error in your post? That is should read SET DOHISTORY TO - with a single S?
Yes, that's a typo, sorry.

Moving on from there, I assume you found that code by using the MESSAGE() function. If so, then SET DOHISTORY TO must appear somewhere in your code. And it must be at that point that the error is being triggered. You say that you don't have it in your code. Have you used Code References to make certain of that?
In Code References, SET DOHISTORY TO - No matches found
 
Chris Miller

I have a feeling you do general error handling with a TRY CATCH in your start/main.prg, that's no good idea.
I have TRY CATCH in a function, for ON ERROR I have a separate function

That's file access problems, and you can get that on both the source file and if you copy to the same destination it can also be a problem of writing to the same file. As already suggested, to do do prevention, stop a second user from copying the same source file, or at least stop them from writing to the same destination.
I want to leave the only way to store the file. However, I do not have registration in the application for
user identification, maybe I can take the computer username in another way? For example, through
CreateObject'WScript.Shell') username?

I don't know what you mean by
Quote (SashaDG)
which file to upload to the Grid
I wanted to say, the user selects a value in the Optiongroup -> the Copy To code is triggered
Code:
TRY	
COPY FILE (placeFrom) TO (placeTo)
successCopied = .T.
CATCH 
successCopied = .F.
ENDTRY


Let me also ask a question: Would you like two users to be able to copy the same file? And does each one have a different target directory?
It sounded to me that there is a backlog of jobs to be done and no matter who does it, it's only to be done once. But maybe my impression about that is wrong.
There is an application on a network drive, the source (placeFrom) is updated with new information several times every day.
And from (placeFrom) at the entrance should be copied to (placeTo). Users on the PC only have a shortcut
 
By the way, removing TRY Catch the error remained the same
Code err 2059. "unhandled structural exceptions" ErrorNo 1705. Message: file access denied. LineNo:70 SSET DOHISTORY TO
 
In Code References, SET DOHISTORY TO - No matches found

As I said, the presence of absence of SET DOHISTORY TO has got nothing to do with the error you are seeing. Focus instead on the actual "File access denied" error.

removing TRY Catch the error remained the same

which suggests that your unhandled exception error was triggered by a different TRY/CATCH, somewhere else in the program. You might try setting a breakpoint and single-stepping to reach the point where the error occurs.

Also, if the error occurs while you are testing your code, that suggests that it is not caused by two users trying to copy the same file, or write to the same destination, at the same time. In fact, I'm not even sure you would get that error when trying to do a file copy. It is usually caused by trying to open a table (a DBF) that is already opened exclusively somewhere else.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
SashaDG,

sorry, I don'T get what you're saying. Please describe again what is the goal of the copying of files.

If this is to update the EXE, as you said
sashaDG said:
Users on the PC only have a shortcut
You can't update an EXE on the network, while it is used even by only one user, that's a problem you have with providing an EXE an the network.
Or is it that the users update the EXE they currently run from a network location to the local location? That never works, when the EXE already runs, i.e. a fie can't be overwritten, when it is currently open, also when it's open because it is executed, and then it can also not be overwritten, even if it is itself that wants to overwrite itself, there is no priviledge of the file itself to overwrite itself.

The solution is to give users a shortcut to a batch file (cmd) that copyies the EXE (plus runtimes) to a local drive and starts it there. Then you can always replace the EXE in the network drive as nobody runs the EXE from there. And even in that situation the user will get problems starting the new version by using the shortcut, if they still run the EXE.

Lete's put it all together in one question:

SashaDG, do you want to inform users of a new version of your application and let them update it from within the application? That will require more things than copying a file, you need to quit and use a secondary EXE or cmd file to update the EXE and restart it.

Also, if you just need new information to be available multiple times a day you should only need to change data, and that's also usually not done by overwriting DBFs, but doing APPEND,INSERT/UPDATE operations on the data.

I really still don't understand what your goal is.

As Mike already observed, when you remove the TRY catch and still get the unhandled structured excception error, there is another TRY CATCH. My suspicion could turn out as correct. Please make use of the cpde references project search to find places of CATCH or CATCH TO and of SET DOHISTORY, I'm sure you'll find where this is located.

Chriss
 
Mikes advice on using the debugger to singlestep through code is also a good advice.

If you have problems causing that error, as it stil only happens to multiple users in special situations, I have the feeling it woud also happen with any other error, so simply put in any code that will error.

Do it this way: In a form of your application add a button that has the click code:
Code:
Use sys(2015)
Set a breakpoint in that line. Open up the debugger before starting main.prg and then start your application main.prg and start the form from the menu, click the button.

The debugger should become active with the click code in the trace window, before that use is executed. Now you can use the step into button or F8 to do the USE that will raise an error. And then you should arrive in the CATCH of the TRY CATCH.

If my suspicion is wrong, you'll get to what is done ON ERROR. Then you may need to put this USE SYS(2015) within the code of the optiongroup to get closer to the situation that normally causes the unhandled structured exception problem.

When you arrive at the CATCH block in the debugger trace window, you can see where this is in the header of the trace window, of course, where object/proceddure is shown. If a PRG runs, this will show nothing. You can always have THIS and PROGAM() as two entries of the Watch window of the debugger, to let the current object be shown or if that shows (expression could not be evaluated) at least the PRG name.

Chriss
 
Chris said:
Please make use of the cpde references project search to find places of CATCH or CATCH TO and of SET DOHISTORY, I'm sure you'll find where this is located.

Always keeping in mind that it might be in a third-party library or other component.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike,

what you're saying is it might not be in the current project? Wouldn't even at hird part library beccome part of a project, if used? The only decoupling that would hide this is an APP or DLL. Or the code is removed from an SCX/VCX where only the ObjectCode memo is kept.

Well, when searching for the source code fails getting there through debugging should be possible, see my last post advice on this, too. I second your thought that debugging could find it, it may just be hard to reproduce the error, so maybe producing any other error will also lead SashaDG there.

Chriss
 
Mike
which suggests that your unhandled exception error was triggered by a different TRY/CATCH, somewhere else in the program. You might try setting a breakpoint and single-stepping to reach the point where the error occurs.
There is another TRY to remove the index file
Code:
DO Cpzero WITH (lcPath),866

LOCAL oExc as Exception
TRY
USE (lcPath)
CATCH TO oExc WHEN oExc.ErrorNo = 1707
USE (lcPath)
ENDTRY
 
Chris
SashaDG, do you want to inform users of a new version of your application and let them update it from within the application? That will require more things than copying a file, you need to quit and use a secondary EXE or cmd file to update the EXE and restart it.

Slightly wrong: I do not update the Exe, the user selects the file name in the form, the application takes
from source(sourceFrom) copies to (sourceTo), removes the link to the index file if necessary and creates a cursor for the Grid

The difficulty is that there is no Debugging for multi-user mode.
And I build the application, then I run two applications on the same computer at run time.
 
One thing is for sure, SashaDG:
The problem of the unhandled structured exception is not depending on the copy file, you can get there with other errors too.

I gave you instructions. To get closer to the point you could do the USE SYS(2015) instead of the COPY FILE. And then you don't depend on two users testing/debugging anymore.

If you want to replicate the exact problem with two sessions you run on your own PC; let one run as EXE and the second one in the IDE, that'll make that session ddebuggable.

Chriss
 
You haven't reported back on the search results you got from code references searches. Did you not say anything because you didn't find anything?
You can and should tackle the problem from multiple sides at the same time, not first deep dive into one approach until it is exhausted and then go for the other suggestion.

If you find the TRY CATCH near/with SET DOHISTORY it will be simple to see why that's triggered or to change it, so you get rid of the problem within error handling iitself and can better tackle to copying problem.

Chriss
 
If you find the TRY CATCH near/with SET DOHISTORY it will be simple to see why that's triggered or to change it, so you get rid of the problem within error handling iitself and can better tackle to copying problem.
The post is at the top
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top