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.
 
SashaDG, you said you removed that TRY CATCH and still got that error, there has to be another TRY CATCH we're looking for now.

Chriss
 
Yes, there are two Try catches:
1 When copying a file
Code:
TRY	
	COPY FILE (placeFrom) TO (placeTo)
	successCopied = .T.
	
	CATCH 
		successCopied = .F.
	ENDTRY
2 When I check for the existence of an index file
Code:
		LOCAL oExc as Exception
		TRY
		    USE (lcPath)
		CATCH TO oExc WHEN oExc.ErrorNo = 1707
		    USE (lcPath)
		ENDTRY
		use
1 I commented out, 2 if I comment out, I get an error


Use sys(2015)
I don't think about it at all, I still don't see the light at the end of the tunnel
 
coderef_ivgsfs.png


searchproject_fi5kbm.png


Also search CATCH and CATCH TO. If you don't find anything, take the advice about debugging.

Also, remember I also already said:
myself said:
The unhandled sructural exception only happens, when you CATCH TO an exception object

See for yourself:
Code:
? "CATCH without TO eception"

Try
  Use sdfjklsd
Catch
  ? Error(),Message()
EndTry


? "CATCH with TO eception"

Try
  Use sdfjklsd
Catch To loException
  ? loException.Error, loException.Message
EndTry

As the unhandled structureed excaption error only comes up if there is an exception object, it's not bound to the exception that happens in both cases, it's strongly coupled to the specific excption object created by the TO clause of catch. And since your TRY CATCH doesn't have such a clause it isn't in itself the source of the unhandled structured exception. This happens due to another TRY CATCH also getting triggered. It must be. It doesn't matter that you didn't program that or don't know abouut it, it must exist and it can be elsewhere in the project as long as it's on the execution stack. That's 100% for sure.

Chriss
 
SashaDG said:
I don't think about it at all, I still don't see the light at the end of the tunnel

Simply do what I instructed. The USE sys(2015) will trigger an error and the breakpoint will transport you into the debugger at the place you will need to change, after you hit F8 once. Especially if you don't find it by code references searching.

Chriss
 
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?

Chris, I was thinking of the case where a third-party library is in another part of the directory tree, and the programmer has ticked "Limit search to project home directory and subfolders". But it's not an important point. Let's not get distracted by it.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
When I check for the existence of an index file

Sasha,

Given that you are trapping error no. 1707 ("Structural .CDX file is not found"), that suggests that you expect the DBF to have a corresponding CDX, and it is that condition that you want to verify. In that case, you can use CDX() to get the name of the index file, and then use FILE() to check for its presence or absence. No need for TRY / CATCH.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I noticed that if the file has no link to the index file, then the multi-user mode works without errors.
I need to break the link with the index file, I can't use cdx() and should I, because I need to open the file
and error 1707 is triggered.
I tried to write errors to the handler and set the Delay to 3 seconds (is it necessary at all)
Error 2059 gone
Code:
PROCEDURE ERRORHND
		=AERROR(AER)  
		ERRORCODE = ERROR()
	DO CASE 
		CASE errorcode = 1707	&&.connect with .cdx
			mainform.text3.value = '1707'
*!*			Delay()
			RETRY 
			
		CASE ERRORCODE = 1705	&&denied
			mainform.text1.value = '1705'
*!*			Delay()
			RETRY 
		CASE ERRORCODE = 2059
			mainform.text2.value = '2059'
*!*			Delay()
		OTHERWISE 

		lnMsgResult = MESSAGEBOX('Code error' + STR(ERRORCODE,4)+CHR(13)+;
		MESSAGE() + [	] + MESSAGE([1]) + CHR(13),0,' Found error ')
	ENDCASE 
RETURN

Code:
FUNCTION Delay()
	PRIVATE dtdelay, dtsecond
	dtdelay = SECONDS()+3
	dtsecond = SECONDS()

	DO WHILE dtsecond < dtdelay
		dtsecond = SECONDS()
	ENDDO

	SET escape OFF 
	WAIT CLEAR 
	WAIT '' WINDOW TIMEOUT 0.1
	SET ESCAPE ON 

	RETURN
ENDFUNC
I think to ensure that the data that is copied is stored separately for each user :(
 
Sasha, lots of issues here.

First, if your ERRORHND procedure is an error handler, called by ON ERROR, then you should not be referencing Mainform (or any other object or variable from outside the error handler). The error handler can be called from anywhere in the application, so you can't be sure that Mainform exists or is in scope at that point.

Also, rather than calling AERROR() within the error handler, it is more usual to pass ERROR(), MESSAGE(), PROGRAM() and LINENO() as parameters. Keep in mind that if another error occurs between the ON ERROR call and the call to AERROR(), the latter will reflect the second error, not the one that triggered the error handler.

If the error that triggers the error handler is not one that you are specifically testing for (1705, 1707, etc), then it is not enough to display the error details in a message box. You need to terminate the program, which might also involve reverting any buffers and rolling back any transactions.

A minor point: Your DELAY() routine seems a bit heavy-handed. You will find the Windows Sleep() function easier and cleaner. To use it, put this line near the start of your main program:

DECLARE Sleep IN WIN32API INTEGER iPeriod

then, instead of calling DELAY(), call Sleep(), passing the number of milliseconds. (Note that the function name is case-sensitive.)

But the main point is that this whole thing seems to have become much too complicated - and has drifted a long way from your original problem. We started talking about unhandled exceptions, and now we are bogged down with links between tables and index files. And I'm still not clear what your overall goal is. Are you trying to apply an update to a running EXE file (as suggested earlier in the thread) or are you trying to copy a DBF with a CDX, or what?

If you could take a deep breath and then try to explain your overall goal, we might be able to suggest the best approach to take.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
If the index problem arises, because you copy a DBF without copying the CDX, too. WEll, that's the problem. And by the way, even if you copy both files, it takes a while and during that time, DBF access is still possible, your copies can be out of sync with the original files.

I'd say it's rarely a good idea to copy a table, even all files of it, to anywhere else, unless it's a template or default data, which is not used in it's origin but only used after it's fully copied.

You could get to the usage pattern you need by copying with other extensions, finally renaming to dbf and cd in one go so there is no earlier usage before both files are there and renamed with the correct extensions.

You could have a table.dbf,tabale.cdx,table.fpt triple of file, rename them all before copying to table.db_,tabale.cd_,table.fp_ do a COPY origin\table.* to destination\table.* and only after all three files are there rename the extensions back to dbf,cdx,fpt.

And yes, you can make such copies of multiple files as COPY firstlocation\xyz.* to otherlocation\xyz.*, VFP understands that the same file skeleton pattern (as ADIR also uses it)

If you copy that way you will only have a problem with out of sync cdxes, but not missing ones. And if the reason of missing cdxes are useers not knowing a table is more than its main dbf file, well, then they may have too much permissions on files, don't they? Your application stability should not depend on dangerous half knowledge of "power users". Maybe that's where your idea stems from to offer a copying utility. Well, but you also need to do it in a way that nothing breaks just because it is transported.

As we started based on the problem this copying happens concurrently is the first hint you didn't think of how delicate it is to do this with an exlusivety. By the way, it is no problem at all to have multiple users copy the same origin file to each different destinations. That's still much easier done with batch files doing xcopy or robocopy, not with VFPs COPY FILE.

Edit: And by the way, all this caution while copying also won't help if the dbf is part of a dbc in the origin and not in the destination. And other yet unnamed problems, like the file will start to exist in the destination while its still copied and another process still can't open it as a dbf, even if the header is complete and just the reords are coming in.

There would be another way of coping data that can work in parallel to using it, if you create an empty dbf in the destination and append data you can work shared and at least read from that dbf in a client while it still builds up with the append. If there is any gain from having partial data as early as you can, then you ccould do it this way, from the inside, not the outside.

Chriss
 
If the aim is to copy a DBF and to ensure that its CDX, if any, is always copied along with it, then the solution is surely to use [tt]COPY TO ... WITH CDX[/tt] rather than [tt]COPY FILE[/tt].

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike
First, if your ERRORHND procedure is an error handler, called by ON ERROR, then you should not be referencing Mainform (or any other object or variable from outside the error handler). The error handler can be called from anywhere in the application, so you can't be sure that Mainform exists or is in scope at that point.
I agree, I made a conclusion for myself


If the error that triggers the error handler is not one that you are specifically testing for (1705, 1707, etc), then it is not enough to display the error details in a message box. You need to terminate the program, which might also involve reverting any buffers and rolling back any transactions.
The application will only view, sort the copied data and transfer to excel. I believe these points will not affect the error(1707 1705)


But the main point is that this whole thing seems to have become much too complicated - and has drifted a long way from your original problem. We started talking about unhandled exceptions, and now we are bogged down with links between tables and index files. And I'm still not clear what your overall goal is.
After seeing Sleep(), I agree that Delay looks bad.
At first there were questions and errors and questions with the multiplayer mode. In the program 2 TRY CATCH. I thought it was in TRY CATCH related
with COPY FILE (it turns out the error here is 1705, but not due to TRY CATCH). You pointed to another TRY CATCH(1707) that opens a file to unlink
with index, index is not necessary to me.


Are you trying to apply an update to a running EXE file (as suggested earlier in the thread) or are you trying to copy a DBF with a CDX, or what?
What does "update running EXE" mean? I want the user in the exe to click on a button,
the file was copied from sourceFrom to sourceTo (I thought that there was an error 1707, but here sometimes 1705 (just solved in errorhand))

Now I want to say that it works. But I would like error 1707 not to be solved by opening the file in Errorhand (this is not what the function is intended for). Iranian error
did not arise when I launched one application and processed through TRY CATCH.
 
sahsaDG said:
What does "update running EXE" mean?

Well, replacing the EXE that runs with a newer version.

The impression came from you saying
sasha said:
There is an application on a network drive, the source (placeFrom) is updated with new information several times every day.
Which could mean the updaed information is within new builds of the EXE. IT surely can also mean there is new data.

But all in all, if you have new data, well, you have new data in your central database, there's nothing to copy over anywhere.

I think both me and Mike still don't fully get what you're tring to do. It would perhaps help if you describe that completely non-technically. Because I'm sure there are better ways to do what you want to do without getting into trouble about multi user concurrency, file access problems, error handler problems and further problems that are further and further away from the actual problem.

If we have iproved your error handling already, that's a good partial outcome. I doubt handling errors in a case statement will be a good treat to them. Especially an unhandled structured exception should not even occur instead of just having a case in the general errorhandler. If you have error 1705 because a user sinply doesn't have file access by file permission management, then retrying also won't help.

Chriss
 
From what I understand, copying with error 1705 by retrying until you can copy would not be right, as a file should only be copied once.

But then, this also may be due to my wrong understanding of your primary goal.

Chriss
 
I'll probably give up on this idea, even though I like it. The fact is if access is closed, then the process loops, and even NO button can not be pressed.
 
Indeed,

you're missing to DOEVENTS, Sleep() does not enable users to do anything. It just enables other processes to go on instead of yours.
We also already proposed the idea to do something in parallel with another EXE.

That could try to copy a file and if it fails try again and again. As that is done in a separate process it wouldn't hinder users to continue working in your application. You just have to think of signalling that the copy has been done and the main application process can then use that file copy in its destionation location.

Chriss
 
Code:
you're missing to DOEVENTS, Sleep() does not mean users an do anything.
Could you be more specific, I don't understand what you mean.
 
Well, I bet you think by Mikes advice, that Sleep() not only delays the retry by the amount of millicceconds before it retries.
You also think during this sleep time the appication will process clicks or other events. It doesn't.
Sleep is also an activity. A bloccking activity to your own process.
You only help other process (completely other applications) to get their time slice of running on the same CPU core as you.

Only DOEVENTS is a VFP command which, if done frequently during a loop, can reestablish that events can happen in parallel to your waiting/retry loop.
There is no real parallel processing in VFP. Also the event model doesn't make VFP multithreaded.

I bet you think the existence of a button on screen and a user clicking it raises the click event right away, always. It doesn't. VFP also has to be in a state awaiting events. READEVENTS is such a state. That's where a VFP application might be 90% of the time. But Sleep() isn't such a state. Executing anything and especially in a loop that only ends after the final try of COPY finally succeeds, means your process becomes unresponsive to interactions and events, it freezes.

Chriss
 
That could try to copy a file and if it fails try again and again. As that is done in a separate process it wouldn't hinder users to continue working in your application. You just have to think of signalling that the copy has been done and the main application process can then use that file copy in its destionation location.
That is, check when the file was last modified, but will I get the same error?
 
sashaDG said:
That is, check when the file was last modified
Not sure what you mean by that, if you check the modifcation date of the destination file, that will already be changed when the copy starts, while you would need to wait for the copy to end.
It's the secondary process that knows best when the copy was completed and then will need to signal to you. That's the better way.

Will you get the same problem of error 1705, why don'T you try. Likely, it's not happening because your process copies the file instead of another, its happening because two sessions on even two different computers try to get at the same file at the same time. And it's still not lear whether the problem is accessing the same source or the same target.

This big advantage of doing the copy in a separate process (running a filecopy.EXE) is that it doesn't matter whether you render that process unresponsive, it has no user interface and needs no interacctions.

Chriss
 
Your second process wouldn't need to care for erros and error numbers tough, you could do as simple as

Code:
Lparameters tcFrom, tcTo

Local llRetry, lcTransit
lcTransit = ForceExt(tcTo,"intransit")
llRetry=.T.
Do While llREtry
   Try 
     COPY FILE (tcFrom) TO (lcTransit)
     llRetry=.F.
   Catch 
     *
   Endtry
Enddo
*Signal copy is done, for example by establishing the right file extension
RENAME (lcTransit) To (Justfname(tcTo))
The main application then waits for tcTo to exist, as copy uses an override for the file extension that will only happen after the copy is done with a RENAME.

You can call that with the two file names as parameters, in several ways, for example with ShellExecute().

If you would do that in the same process you want users to be able to continue working while that copy is done, you block exactly that, any VFP process is singlethreaded and won't do things in parallel.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top