This is a crosspost from the md5classic Macintosh Garden page.
I have uncovered an absolutely insane issue with Classic Mode in (at least) 10.4 Tiger. As far as I can tell, it doesn't fully support asynchronous file reads.
According to Apple, while an asynchronous read with
The problem is that
will run infinitely.
HOWEVER, if you first read the file into memory with something like Resorcerer, then a subsequent asynchronous read of the file will be perfect.
I created an application (HFSPlusAsyncTest) to easily test for the bug. This archive contains both a Classic and Carbon version. The Carbon version will run fine in OS X, but will show the bug in Classic Mode. Source code for the Classic version is here.
Here's some sample output opening a file for the first time:
------
And then here is the output opening the same file right after:
------
You can see in the first instance that the value 1c4644ef6d27de0 is repeated. This is because it's reusing the previous data buffer for that thread (#2). This is not good for hashing.
Anyway, all this is to say that Classic Mode is killing me. If I add in any checks to the code then it's going to slow it down considerably. I scoured the internet for anyone who has come across this bug and I couldn't find anything. Do I somehow test for Classic Mode and just use synchronous reads? Has anyone successfully used asynchronous reads in Classic Mode?
I have uncovered an absolutely insane issue with Classic Mode in (at least) 10.4 Tiger. As far as I can tell, it doesn't fully support asynchronous file reads.
According to Apple, while an asynchronous read with
PBReadForkAsync(FSForkIOParamPtr fsForkPtr)
is executing, we can continuously poll fsForkPtr->ioResult
If it's a positive value, the task is still running, anything else and the task has completed and data is ready to manipulate. (Documentation for PBReadForkAsync here).The problem is that
fsForkPtr->ioResult
can stop updating non-deterministically. If that happens, the standard loop:while (fsForkPtr->ioResult > 0) {};
will run infinitely.
HOWEVER, if you first read the file into memory with something like Resorcerer, then a subsequent asynchronous read of the file will be perfect.
I created an application (HFSPlusAsyncTest) to easily test for the bug. This archive contains both a Classic and Carbon version. The Carbon version will run fine in OS X, but will show the bug in Classic Mode. Source code for the Classic version is here.
Here's some sample output opening a file for the first time:
Code:
File: ***
Data Fork Size: 1536133 bytes
[Thread 1] Waiting for data (1)
[Thread 1][0] 32768 bytes read. Mark now @00008000. Data: 5349542100010017
[Thread 2] Waiting for data (1)
... (total of 10 loops)
[Thread 0] Waited too long!
[Thread 2][1] 0 bytes read. Mark now @00000000. Data: 0
[Thread 1][2] 32768 bytes read. Mark now @00010000. Data: f14d29a5552b0f5c
[Thread 2][3] 32768 bytes read. Mark now @00018000. Data: a815036e15ff0b48
[Thread 1][4] 32768 bytes read. Mark now @00020000. Data: c7efc0fa3938c10a
[Thread 2][5] 32768 bytes read. Mark now @00028000. Data: 1c4644ef6d27de0
[Thread 1] Waiting for data (1)
[Thread 1] Waiting for data (1)
[Thread 1] Waiting for data (1)
[Thread 1][6] 32768 bytes read. Mark now @00030000. Data: 9efb957338ca6667
[Thread 2] Waiting for data (1)
... (total of 10 loops)
[Thread 0] Waited too long!
[Thread 2][7] 32768 bytes read. Mark now @00028000. Data: 1c4644ef6d27de0
[Thread 1][8] 32768 bytes read. Mark now @00038000. Data: 79545654b2d0573
...
[Thread 1][48] 28805 bytes read. Mark now @00177085. Data: de8e3ae1191475da
[Thread 1] ioResult was -39 (EOF)
------
And then here is the output opening the same file right after:
------
Code:
File: ***
Data Fork Size: 1536133 bytes
[Thread 1][0] 32768 bytes read. Mark now @00008000. Data: 5349542100010017
[Thread 2][1] 32768 bytes read. Mark now @00010000. Data: f14d29a5552b0f5c
[Thread 1][2] 32768 bytes read. Mark now @00018000. Data: a815036e15ff0b48
[Thread 2][3] 32768 bytes read. Mark now @00020000. Data: c7efc0fa3938c10a
[Thread 1][4] 32768 bytes read. Mark now @00028000. Data: 1c4644ef6d27de0
[Thread 2][5] 32768 bytes read. Mark now @00030000. Data: 9efb957338ca6667
[Thread 1][6] 32768 bytes read. Mark now @00038000. Data: 79545654b2d0573
[Thread 2][7] 32768 bytes read. Mark now @00040000. Data: 8099ab9ef4ea2ce4
[Thread 1][8] 32768 bytes read. Mark now @00048000. Data: 3f3c8dffdcd87bfb
...
[Thread 1][46] 28805 bytes read. Mark now @00177085. Data: de8e3ae1191475da
[Thread 1] ioResult was -39 (EOF)
You can see in the first instance that the value 1c4644ef6d27de0 is repeated. This is because it's reusing the previous data buffer for that thread (#2). This is not good for hashing.
Anyway, all this is to say that Classic Mode is killing me. If I add in any checks to the code then it's going to slow it down considerably. I scoured the internet for anyone who has come across this bug and I couldn't find anything. Do I somehow test for Classic Mode and just use synchronous reads? Has anyone successfully used asynchronous reads in Classic Mode?