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 );
}