Hi to all,
I am new in this forum. I was searching help about a recent problem i have got with filestream object.
Specifically I have the need that two or more users read and write a file content and so I coded these lines of code:

. . .
. . .  this code is in a function.. for simplicity I reported a snippet
public enum 	dbLockType	{ _none =0, _lock, _unLock, _default}
long lockPos = -1;
long lockRec = -1;
int  blockSize = 100;
 
  FileStream DtFile = new FileStream(@"\\server\d\folder\test.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite,8000); 
. . .
. . .

public bool getBufferFromDisk(long RecordNumber, byte[] toArray, int startingAt, int length, dbLockType dbLock)
{
  long seekPosition = GetFilePos(RecordNumber); // calc RecordNumber*blockSize
  try {
	DtFile .Seek(seekPosition, System.IO.SeekOrigin.Begin);
	DtFile .Read(toArray, startingAt, length);
	if ( dbLock == dbLockType._lock) {
  	   if ( lockPos > -1)
		DtFile .Unlock(lockPos, blockSize);  // unlock previous locks
	   DtFile .Lock(seekPosition, blockSize); 
	   
	   lockPos = seekPosition;
	   lockRec = RecordNumber;
	}
  }
  catch(IOException e) {
	return false;	
  }
  return true;
}

After that the first user open the file and call the getBufferFromDisk with these parms:

getBufferFromDisk( 8, toArray, 0, 100, dbLock);

in this way it lock the filestream at pos (7 x100) for 100 bytes.


the second user try to open the file and to execute the same function with these parms:
getBufferFromDisk( 0, toArray, 0, 100, dbLock);

at this point I got an error telling me that the file is partially locked by other process.
I found the prorblem occur at this line :
DtFile .Read(toArray, startingAt, length);

Stange , because I have locked 100 bytes starting from pos 700 not at 0.

After some reading I found this article that seems more like my problem : http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic15633.aspx

So I tried to go further and after some investigation I found :
1. Even though I change the FileAccess to .Read ( while the first pgm is locking the region) the second user find the record at pos 0 locked.
2. the lock is extended to the whole buffer , that is 8K.
3. all this is true if the second user try to open and read something in this buffer if something in this buffer is locked, otherwise all is ok!, I mean if the first user lock something at pos 8500 , 100 the second user has no problem at all.

What is this , a bug? Am I wrong in something ? this is the correct way to lock-unlock regions ?.

Could someone help me to get rid of this problem ?

Thanks to all ?

JossGP

Recommended Answers

All 17 Replies

You dont ever seem to unlock your locks.
Id expect the lock to be pre-read, and the unlock post read.

thank LizR for reply.
Probably I have not been clear. The problem is not that I want/must to unlock the lock of the first user, but that the OS keeps lock an area that should not be locked, and the worst is that it keeps locks the whole buffer.

If you read the post at the link is explained very well.
I was looking for news about this problem ? , if it is true , of course.

I think you missed my point.
You told it to lock that part of the disk, so the second user *CANT* get at it, you locked it with the first guy and never released it. The OS would also potentially do the same, however, it shouldnt take that long, and should/could be retried. if after a given number of attempts its still failed - somethings wrong.

No, I don't think, I just WANT that the pos 700 for 100 bytes keeps lock by the first user. the OS seems to keeps lock 8k, the whole buffer. Where in my code I told the OS to keeps lock 8k ?. I expected that the OS tell me about an active lock just if I try to read the same pos ( 700x100). As I write, in any case if try to read whatever else position between 0 and 8k the OS tell me that is locked.
I run this example just to test if there was any limit in these functions and unluckily seems i have found one.

Then something else is fundementally wrong.

As:

byte[] data=new byte[1025];
            FileStream fs = new FileStream("c:\\nokia.txt", FileMode.Open,  FileAccess.ReadWrite, FileShare.ReadWrite, 8192);
            fs.Seek(0, System.IO.SeekOrigin.Begin);
            fs.Read(data, 0, 10);
            Thread.Sleep(10000);
            fs.Close();

when ran 5 times in succession, all copies succeeded.

So, it is the locking thats causing the issue, because if you lock part of it the errror about "The process cannot access the file because another process has locked a portion of the file." is generated and its right.

So the read itself is not the issue, its the lock, the lock is being detected and preventing all reads, but thats how i originally expected it to be designed. The example on MSDN clearly shows only the locking while writing, which is what id expect.

Apparently MS respond with:
http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic15633.aspx

Would suggest if you need record locking, use a database.

Thank you LizR. I want you to know that this snippet is part of a project that I am writing which is a database. I wrote for years code in c++ using BDE. Now I would like to create a C# DB using the Berkeley style + foxpro style ( i mean with no SQL ) all in managed code, but I stumble in this rock and i don't know how to workaround it.

Thank you for you help.

Ok, now, oddly, if I ensure that its not locking /reading the same part of the file, all works well.

eg I added a parameter, which told it where in the stream to move to, so, its only locking for me the area I tell it to.

I think I know why

in your code you have

DtFile .Seek(seekPosition, System.IO.SeekOrigin.Begin);
	DtFile .Read(toArray, startingAt, length);

But your startingAt should be 0...... you dont want to offset from where you are... You also may just wanna change the System.IO.SeekOrigin.begin to 0.. just to make it clear.

Like that I could even reuse the same area.. and read it.

Yes. the startingAt is always 0, infact in these lines:
getBufferFromDisk( 8, toArray, 0, 100, dbLock); .. and
getBufferFromDisk( 0, toArray, 0, 100, dbLock);
the staringat pos is 0. I chenged from 8 to 0 the record number.

In all cases I move the seekPosition which is calculated ( RecordNumber * blockSize + headerSize ).

I didn't tell you before. the headeSize is a little fixed area i need to read in order to have base file information like active records etc.. )

It is just this header size part of the problem because i need to reed it in any case to initialize the db file.

OK, but as you may have read, it works for me. So as long as your seekposition is correct and doesnt end up with overlaps, I had no issues in multiple reads of even the same locked area.

I don't know what do you mean when you say you have no issue on multiple reads. If you want to say without locks , I agree with you.

I don't want you spend much of your time , but if you try the example I posted surely you are going to get the same exception I got. And also as I reported the OS locks the whole buffer.

If I well understood your post and if you say you have " no issues in multiple reads of even the same locked area" , Could you tell me how I have to right code the function ?. knowing that in any case I need to read an header buffer even though the file buffer or part of it is locked by other users ?.

I mean , if you know a solution, because my think is that is an OS bug until the contrary.

Sorry for my English. Hope you understand me.

I spent around 2 hours this afternoon playing with it.

I have told you how I changed your code and it workde.

sorry , don't get me wrong. I appreciate your time. I just woult like to solve my problem.

In your example there is no Lock functions. I also run your code and it run perfectly for n times. but if you change it this way:


byte[] data=new byte[1025];
FileStream fs = new FileStream("c:\\nokia.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 8192);
fs.Seek(0, System.IO.SeekOrigin.Begin);
fs.Read(data, 0, 10);
fs.Lock( 0,10)
Thread.Sleep(500000);

than leave this program running in current session.
than open a new ide session change the code this way and run it:

byte[] data=new byte[1025];
FileStream fs = new FileStream("c:\\nokia.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 8192);

fs.Seek(150, System.IO.SeekOrigin.Begin);
fs.Read(data, 0, 10); <- what happens here
fs.Lock( 150,10)

.. better is if you reverse the position , in first session
fs.Seek(150, System.IO.SeekOrigin.Begin);
fs.Read(data, 0, 10);
fs.Lock( 150,10);

in the second session :
fs.Seek(0, System.IO.SeekOrigin.Begin);
fs.Read(data, 0, 10); <- what happens here
fs.Lock( 0,10);

Thank you very much for you time. I just want a hand to solve the problem.

I changed mine, as I said.. so that it used a parameter to get the nth record, I then also added back in the locks round the timer, and all was well.

You havent provided all your code, the only other thing I can think of is that your position calculations maybe wrong.

I dont have the code to hand right now, as Im at home, but, I put in a number of catch statements and the only one that was failing was the lock.

ok , Let me prepare a consistent piece of code and I'll post to you.

hear you later. thanks!

Hi , LizR.
I Have prepared a little solution that you find in attached file. ( I used sharpdevelop with .net20 ). the code has't a good behavior but is the most I could do in short time.

there, you find all the code necessary to do the test we are talking about.
to tray follow these steps:
1. open the file
2. type in RecNo field numer 9, 10 or whatever you like ( inside the buffersize 8192) and then click read. The function read ,lock the region and display the phisical lock position.

After that ,open a second instance of the pgm and try yourself what happen.

Let me Know about it. Thank you very much for your time and for the help you tried to give me.

oops, sorry, I gave you wrong code. this is the last.

.
.
long seekPosition = GetFilePos(RecordNumber);
if ( dbLock == dbLockType._lock) {
     if ( lockPos > -1)
	 streamFile.Unlock(lockPos, blockSize);
	 lockPos = -1;
}
try {
	streamFile.Seek(seekPosition, System.IO.SeekOrigin.Begin);
	streamFile.Read(toArray, startingAt, length);
	if ( dbLock == dbLockType._lock) {
	   //if ( lockPos > -1)
	   //	 streamFile.Unlock(lockPos, blockSize);
	   streamFile.Lock(seekPosition, blockSize); 
	   //if ( seekPosition >= 0)
	       System.Diagnostics.Debug.WriteLine("header block "+seekPosition.ToString() );
	   lockPos = seekPosition;
	   lockRec = RecordNumber;
       }
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.