Re: FSMountServerVolumeAsync() in multiple threads?
Re: FSMountServerVolumeAsync() in multiple threads?
- Subject: Re: FSMountServerVolumeAsync() in multiple threads?
- From: Matt DeFoor <email@hidden>
- Date: Thu, 13 Aug 2009 17:04:22 -0400
Jim - Thanks for the sample code! It has helped quite a bit! I think
that it has helped me identify some potential bugs in
FSMountServerVolumeAsync(). The bugs appear to be related to the
protocol being used and whether or not the thread is detached. In my
application, all threads are created by NSThread and are thus,
detached. The only way that I can get reliable mounting of AFP volumes
via threaded calls to FSMountServerVolumeAsync() is to start the
thread and then join it to the creating thread (just like in your
sample). If you do not call pthread_join(), then you get varying
results from out right failures (-50, -36, to name a few) to crashes.
My understanding of pthread_join() is that it joins the detached
thread to the thread it is called from and the calling thread waits
for the newly created thread to finish. This is somewhat counter
productive to the usage of FSMountServerVolumeAsync() when the server
is unavailable. It makes the calling thread block. You might want to
try this and see if you can reproduce these results. I've tried it
with your code on two different AFP servers (AFP on Mac OS X Server
and AFP on NetWare) to see if one behaves different than the other.
They do not. They both produce sporadic results and failures.
Anyway, I wanted to let you know what my findings were. It sounds like
I need to file a bug...unless there are other suggestions.
Cheers,
Matt
On Wed, Aug 12, 2009 at 2:47 PM, Jim Luther<email@hidden> wrote:
> On Aug 11, 2009, at 5:28 PM, Matt DeFoor wrote:
>
>> I have an application where I'm trying to mount multiple volumes in
>> their own thread using FSMountServerVolumeAsync(). The application is
>> Cocoa-based. So, I use:
>>
>> [NSThread detachNewThreadSelector:@selector(mountResource:)
>> toTarget:self withObject:aResource]
>>
>> to spawn each thread. The call to FSMountServerVolumeAsync() looks like
>> this:
>>
>> Result = FSMountServerVolumeAsync((CFURLRef)urlOfVolumeToMount, NULL,
>> (CFStringRef)[aResource username], (CFStringRef)[aResource password],
>> volumeOp, aResource, 0, mountUPP, CFRunLoopGetCurrent(),
>> CFSTR("MyMountMode"));
>>
>> As you can tell, I'm using a callback. It handles certain aspects of
>> what is returned. When, I have only one thread, things work fine; the
>> callback is called and the volume is mounted. When I have more than
>> one volume to mount (ergo, more than one thread is calling
>> FSMountServerVolumeAsync()) varying results are produced. For example,
>> the return code from FSMountServerVolumeAsync() might be -50. Or there
>> might be an exception (EXC_BAD_ACCESS) in one of the functions that
>> FSMountServerVolumeAsync() calls (e.g. TAFPSession::AFPLogout).
>>
>> Has anyone had success using FSMountServerVolumeAsync() in this manner?
>>
>> Is there a better approach to FSMountServerVolumeAsync()'s usage?
>>
>> Thanks,
>> Matt
>>
>> P.S. Jim Luther suggest that I post here instead of the macnetworkprog
>> list.
>
> Matt,
>
> I slapped together a quick tool that calls FSMountServerVolumeAsync() on
> multiple threads. I didn't find that FSMountServerVolumeAsync crashes or
> returns errors (well... it will return errors if there's really a problem).
>
> I've pasted the source for my tool below (you'll have to change gMountInfos
> to the URLs you are using). Maybe it will help you figure out why your code
> doesn't work.
>
> - Jim
>
> #include <CoreServices/CoreServices.h>
> #include <pthread.h>
>
>
> struct MountInfo {
> const char *url;
> const char *user;
> const char *password;
> };
>
> // array of server URLs to mount with usernames and passwords
> const struct MountInfo gMountInfos[] = {
> {"afp://foo1.bar/volume", "user1", "password1"},
> {"afp://foo2.bar/volume", "user2", "password2"},
> {"afp://foo3.bar/volume", "user3", "password3"},
> {NULL, NULL, NULL}, // mark the end with NULLs
> };
>
>
>
> static void FSVolumeMountProc(FSVolumeOperation volumeOp, void *clientData,
> OSStatus err, FSVolumeRefNum mountedVolumeRefNum)
> {
> Boolean *inProgress = (Boolean *)clientData;
>
> // all we're doing is setting inProgress (what clientData points to)
> to err so the tool can fall out of the wait loop.
> *inProgress = FALSE;
> }
>
>
> static void DoMountServer(void *arg)
> {
> struct MountInfo *mountInfo;
> pthread_t tid;
> CFURLRef url = NULL;
> CFStringRef user = NULL;
> CFStringRef password = NULL;
> OSStatus status;
> FSVolumeMountUPP callback;
> CFRunLoopRef runloop;
> FSVolumeOperation volumeOp;
> FSMountStatus mountStatus;
> OSStatus volumeOpStatus;
> FSVolumeRefNum mountedVolumeRefNum;
> Boolean inProgress;
>
> mountInfo = (struct MountInfo *)arg;
> tid = pthread_self();
> printf("[%p] mounting \'%s\', \'%s\', \'%s\'\n", tid, mountInfo->url,
> mountInfo->user, mountInfo->password);
> fflush(stdout);
>
> // create the CF objects from the c strings */
> url = CFURLCreateWithBytes(kCFAllocatorDefault, (UInt8
> *)mountInfo->url, (CFIndex)strlen(mountInfo->url), kCFStringEncodingUTF8,
> NULL);
> if ( url != NULL ) {
> if ( strlen(mountInfo->user) ) {
> user = CFStringCreateWithBytes(kCFAllocatorDefault,
> (UInt8 *)mountInfo->user, (CFIndex)strlen(mountInfo->user),
> kCFStringEncodingUTF8, FALSE);
> }
>
> if ( strlen(mountInfo->password) ) {
> password =
> CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8 *)mountInfo->password,
> (CFIndex)strlen(mountInfo->password), kCFStringEncodingUTF8, FALSE);
> }
>
> // create the callback UPP
> callback = NewFSVolumeMountUPP(FSVolumeMountProc);
>
> // get the current runloop to run on
> runloop = CFRunLoopGetCurrent();
>
> // create the volume operation
> status = FSCreateVolumeOperation(&volumeOp);
> if ( status == noErr ) {
> // start the asynchronous mount
> inProgress = TRUE; // set inProgress to "in
> progress"
> status = FSMountServerVolumeAsync(url, NULL, user,
> password, volumeOp, (void *)&inProgress, kNilOptions, callback, runloop,
> kCFRunLoopDefaultMode);
> if ( status == noErr ) {
> // wait loop -- wait for inProgress to be
> FALSE
> do
> {
>
> CFRunLoopRunInMode(kCFRunLoopDefaultMode, 5.0, true);
> } while ( inProgress );
>
> status = FSGetAsyncMountStatus(volumeOp,
> &mountStatus, &volumeOpStatus, &mountedVolumeRefNum, NULL);
> if ( status == noErr ) {
> printf("[%p] mountStatus = %d;
> volumeOpStatus = %d; mountedVolumeRefNum = %hd\n", tid, mountStatus,
> volumeOpStatus, mountedVolumeRefNum);
> }
> }
> FSDisposeVolumeOperation(volumeOp);
> }
>
> if ( password != NULL ) {
> CFRelease(password);
> }
> if ( user != NULL ) {
> CFRelease(user);
> }
> CFRelease(url);
> }
> return;
> }
>
>
> int main (int argc, const char * argv[])
> {
> // make the threadID array big enough for all tids
> pthread_t threadID[(sizeof(gMountInfos) / sizeof(struct MountInfo)) -
> 1];
> int index;
>
> index = 0;
>
> // start a thread for each element in gMountInfos
> while (gMountInfos[index].url != NULL) {
> if ( pthread_create(&threadID[index], NULL, (void
> *)DoMountServer, (void *)&gMountInfos[index]) != 0 ) {
> return ( EXIT_FAILURE );
> }
>
> ++index;
> }
>
> // wait for all threads to complete
> while ( index >= 0 ) {
> pthread_join(threadID[index], NULL);
> --index;
> }
> printf("done\n");
>
> return ( EXIT_SUCCESS );
> }
>
>
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Filesystem-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden