зеркало из https://github.com/mozilla/gecko-dev.git
Bug 77672, fix nsLDAPConnection leaking objects and threads. Patch=leif,
r=dmose, sr=darin, a=chofmann.
This commit is contained in:
Родитель
4f543edb38
Коммит
85e18ae7b4
|
@ -18,6 +18,11 @@
|
||||||
* Rights Reserved.
|
* Rights Reserved.
|
||||||
*
|
*
|
||||||
* Contributor(s): Dan Mosedale <dmose@mozilla.org> (original author)
|
* Contributor(s): Dan Mosedale <dmose@mozilla.org> (original author)
|
||||||
|
* Leif Hedstrom <leif@netscape.com>
|
||||||
|
* Kipp Hickman <kipp@netscape.com>
|
||||||
|
* Warren Harris <warren@netscape.com>
|
||||||
|
* Dan Matejka <danm@netscape.com>
|
||||||
|
*
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the
|
* Alternatively, the contents of this file may be used under the
|
||||||
* terms of the GNU General Public License Version 2 or later (the
|
* terms of the GNU General Public License Version 2 or later (the
|
||||||
|
@ -50,7 +55,9 @@ extern "C" int nsLDAPThreadFuncsInit(LDAP *aLDAP);
|
||||||
//
|
//
|
||||||
nsLDAPConnection::nsLDAPConnection()
|
nsLDAPConnection::nsLDAPConnection()
|
||||||
: mConnectionHandle(0),
|
: mConnectionHandle(0),
|
||||||
mPendingOperations(0)
|
mBindName(0),
|
||||||
|
mPendingOperations(0),
|
||||||
|
mRunnable(0)
|
||||||
{
|
{
|
||||||
NS_INIT_ISUPPORTS();
|
NS_INIT_ISUPPORTS();
|
||||||
}
|
}
|
||||||
|
@ -63,14 +70,24 @@ nsLDAPConnection::~nsLDAPConnection()
|
||||||
|
|
||||||
PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("unbinding\n"));
|
PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("unbinding\n"));
|
||||||
|
|
||||||
rc = ldap_unbind_s(mConnectionHandle);
|
if (mConnectionHandle) {
|
||||||
if (rc != LDAP_SUCCESS) {
|
rc = ldap_unbind_s(mConnectionHandle);
|
||||||
PR_LOG(gLDAPLogModule, PR_LOG_WARNING,
|
#ifdef PR_LOGGING
|
||||||
("nsLDAPConnection::~nsLDAPConnection: %s\n",
|
if (rc != LDAP_SUCCESS) {
|
||||||
ldap_err2string(rc)));
|
if (gLDAPLogModule) {
|
||||||
|
PR_LOG(gLDAPLogModule, PR_LOG_WARNING,
|
||||||
|
("nsLDAPConnection::~nsLDAPConnection: %s\n",
|
||||||
|
ldap_err2string(rc)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("unbound\n"));
|
#ifdef PR_LOGGING
|
||||||
|
if (gLDAPLogModule) {
|
||||||
|
PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("unbound\n"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mBindName) {
|
if (mBindName) {
|
||||||
delete mBindName;
|
delete mBindName;
|
||||||
|
@ -79,10 +96,60 @@ nsLDAPConnection::~nsLDAPConnection()
|
||||||
if (mPendingOperations) {
|
if (mPendingOperations) {
|
||||||
delete mPendingOperations;
|
delete mPendingOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IF_RELEASE(mRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need our own Release() here, so that we can lock around the delete.
|
||||||
|
// This is needed to avoid a race condition with the weak reference to us,
|
||||||
|
// which is used in nsLDAPConnectionLoop. A problem could occur if the
|
||||||
|
// nsLDAPConnection gets destroyed while do_QueryReferent() is called,
|
||||||
|
// since converting to the strong reference isn't MT safe.
|
||||||
|
//
|
||||||
|
NS_IMPL_THREADSAFE_ADDREF(nsLDAPConnection);
|
||||||
|
NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsLDAPConnection, nsILDAPConnection,
|
||||||
|
nsISupportsWeakReference);
|
||||||
|
|
||||||
|
nsrefcnt
|
||||||
|
nsLDAPConnection::Release(void)
|
||||||
|
{
|
||||||
|
nsrefcnt count;
|
||||||
|
|
||||||
|
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||||
|
count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
|
||||||
|
NS_LOG_RELEASE(this, count, "nsLDAPConnection");
|
||||||
|
if (0 == count) {
|
||||||
|
// As commented by danm: In the object's destructor, if by some
|
||||||
|
// convoluted, indirect means it happens to run into some code
|
||||||
|
// that temporarily references it (addref/release), then if the
|
||||||
|
// refcount had been left at 0 the unexpected release would
|
||||||
|
// attempt to reenter the object's destructor.
|
||||||
|
//
|
||||||
|
mRefCnt = 1; /* stabilize */
|
||||||
|
|
||||||
|
// If we have a mRunnable object, we need to make sure to lock it's
|
||||||
|
// mLock before we try to DELETE. This is to avoid a race condition.
|
||||||
|
// We also make sure to keep a strong reference to the runnable
|
||||||
|
// object, to make sure it doesn't get GCed from underneath us,
|
||||||
|
// while we are still holding a lock for instance.
|
||||||
|
//
|
||||||
|
if (mRunnable && mRunnable->mLock) {
|
||||||
|
nsLDAPConnectionLoop *runnable = mRunnable;
|
||||||
|
|
||||||
|
NS_ADDREF(runnable);
|
||||||
|
PR_Lock(runnable->mLock);
|
||||||
|
NS_DELETEXPCOM(this);
|
||||||
|
PR_Unlock(runnable->mLock);
|
||||||
|
NS_RELEASE(runnable);
|
||||||
|
} else {
|
||||||
|
NS_DELETEXPCOM(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsLDAPConnection, nsILDAPConnection,
|
|
||||||
nsIRunnable);
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsLDAPConnection::Init(const char *aHost, PRInt16 aPort,
|
nsLDAPConnection::Init(const char *aHost, PRInt16 aPort,
|
||||||
|
@ -154,10 +221,35 @@ nsLDAPConnection::Init(const char *aHost, PRInt16 aPort,
|
||||||
NS_REINTERPRET_CAST(void *, 0));
|
NS_REINTERPRET_CAST(void *, 0));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Create a new runnable object, and increment the refcnt. The
|
||||||
|
// thread will also hold a strong ref to the runnable, but we need
|
||||||
|
// to make sure it doesn't get destructed until we are done with
|
||||||
|
// all locking etc. in nsLDAPConnection::Release().
|
||||||
|
//
|
||||||
|
mRunnable = new nsLDAPConnectionLoop();
|
||||||
|
NS_ADDREF(mRunnable);
|
||||||
|
rv = mRunnable->Init();
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we keep a weak reference in the runnable object to the
|
||||||
|
// nsLDAPConnection ("this"). This avoids the problem where a connection
|
||||||
|
// can't get destructed because of the new thread keeping a strong
|
||||||
|
// reference to it. It also helps us know when we need to exit the new
|
||||||
|
// thread: when we can't convert the weak reference to a strong ref, we
|
||||||
|
// know that the nsLDAPConnection object is gone, and we need to stop
|
||||||
|
// the thread running.
|
||||||
|
//
|
||||||
|
nsCOMPtr<nsILDAPConnection> conn = NS_STATIC_CAST(nsILDAPConnection *,
|
||||||
|
this);
|
||||||
|
mRunnable->mWeakConn = do_GetWeakReference(conn);
|
||||||
|
|
||||||
// kick off a thread for result listening and marshalling
|
// kick off a thread for result listening and marshalling
|
||||||
// XXXdmose - should this be JOINABLE?
|
// XXXdmose - should this be JOINABLE?
|
||||||
//
|
//
|
||||||
rv = NS_NewThread(getter_AddRefs(mThread), this, 0, PR_UNJOINABLE_THREAD);
|
rv = NS_NewThread(getter_AddRefs(mThread), mRunnable, 0,
|
||||||
|
PR_UNJOINABLE_THREAD);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
@ -335,204 +427,6 @@ nsLDAPConnection::RemovePendingOperation(nsILDAPOperation *aOperation)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for nsIRunnable. this thread spins in ldap_result() awaiting the next
|
|
||||||
// message. once one arrives, it dispatches it to the nsILDAPMessageListener
|
|
||||||
// on the main thread.
|
|
||||||
//
|
|
||||||
// XXX do all returns from this function need to do thread cleanup?
|
|
||||||
//
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsLDAPConnection::Run(void)
|
|
||||||
{
|
|
||||||
int lderrno;
|
|
||||||
nsresult rv;
|
|
||||||
PRInt32 returnCode;
|
|
||||||
LDAPMessage *msgHandle;
|
|
||||||
nsCOMPtr<nsILDAPMessage> msg;
|
|
||||||
|
|
||||||
PR_LOG(gLDAPLogModule, PR_LOG_DEBUG,
|
|
||||||
("nsLDAPConnection::Run() entered\n"));
|
|
||||||
|
|
||||||
// get the console service so we can log messages
|
|
||||||
//
|
|
||||||
nsCOMPtr<nsIConsoleService> consoleSvc =
|
|
||||||
do_GetService(kConsoleServiceContractId, &rv);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_ERROR("nsLDAPConnection::Run() couldn't get console service");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the thread-specific data for the child thread (as necessary)
|
|
||||||
//
|
|
||||||
if (!nsLDAPThreadDataInit()) {
|
|
||||||
NS_ERROR("nsLDAPConnection::Run() couldn't initialize "
|
|
||||||
"thread-specific data");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for results
|
|
||||||
//
|
|
||||||
while(1) {
|
|
||||||
|
|
||||||
PRBool operationFinished = PR_TRUE;
|
|
||||||
|
|
||||||
// in case something went wrong on the last iteration, be sure to
|
|
||||||
// cause nsCOMPtr to release the message before going to sleep in
|
|
||||||
// ldap_result
|
|
||||||
//
|
|
||||||
msg = 0;
|
|
||||||
|
|
||||||
// XXX deal with timeouts better
|
|
||||||
//
|
|
||||||
returnCode = ldap_result(mConnectionHandle, LDAP_RES_ANY,
|
|
||||||
LDAP_MSG_ONE, LDAP_NO_LIMIT, &msgHandle);
|
|
||||||
|
|
||||||
// if we didn't error or timeout, create an nsILDAPMessage
|
|
||||||
//
|
|
||||||
switch (returnCode) {
|
|
||||||
|
|
||||||
case 0: // timeout
|
|
||||||
|
|
||||||
// the connection may not exist yet. sleep for a while
|
|
||||||
// and try again
|
|
||||||
//
|
|
||||||
PR_LOG(gLDAPLogModule, PR_LOG_WARNING,
|
|
||||||
("ldap_result() timed out.\n"));
|
|
||||||
PR_Sleep(2000); // XXXdmose - reasonable timeslice?
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case -1: // something went wrong
|
|
||||||
|
|
||||||
lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0);
|
|
||||||
|
|
||||||
switch (lderrno) {
|
|
||||||
|
|
||||||
case LDAP_SERVER_DOWN:
|
|
||||||
// XXXreconnect or fail ?
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LDAP_DECODING_ERROR:
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: WARNING: decoding error; possible corrupt data received").get());
|
|
||||||
NS_WARNING("nsLDAPConnection::Run(): ldaperrno = "
|
|
||||||
"LDAP_DECODING_ERROR after ldap_result()");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LDAP_NO_MEMORY:
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: ERROR: couldn't allocate memory while getting async operation result").get());
|
|
||||||
// punt and hope things work out better next time around
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// shouldn't happen; internal error
|
|
||||||
//
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: DEBUG: ldaperrno set to unexpected value after ldap_result() call in nsLDAPConnection::Run()").get());
|
|
||||||
NS_WARNING("nsLDAPConnection::Run(): ldaperrno set to "
|
|
||||||
"unexpected value after ldap_result() "
|
|
||||||
"call in nsLDAPConnection::Run()");
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LDAP_RES_SEARCH_ENTRY:
|
|
||||||
case LDAP_RES_SEARCH_REFERENCE:
|
|
||||||
// XXX what should we do with LDAP_RES_SEARCH_EXTENDED?
|
|
||||||
|
|
||||||
// not done yet, so we shouldn't remove the op from the conn q
|
|
||||||
operationFinished = PR_FALSE;
|
|
||||||
|
|
||||||
// fall through to default case
|
|
||||||
|
|
||||||
default: // initialize the message and call the callback
|
|
||||||
|
|
||||||
// we want nsLDAPMessage specifically, not a compatible, since
|
|
||||||
// we're sharing native objects used by the LDAP C SDK
|
|
||||||
//
|
|
||||||
nsLDAPMessage *rawMsg = new nsLDAPMessage();
|
|
||||||
if (!rawMsg) {
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: ERROR: couldn't allocate memory for new LDAP message; search entry dropped").get());
|
|
||||||
// punt and hope things work out better next time around
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the message, using a protected method not available
|
|
||||||
// through nsILDAPMessage (which is why we need the raw pointer)
|
|
||||||
//
|
|
||||||
rv = rawMsg->Init(this, msgHandle);
|
|
||||||
|
|
||||||
switch (rv) {
|
|
||||||
|
|
||||||
case NS_OK:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NS_ERROR_LDAP_DECODING_ERROR:
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: WARNING: decoding error; possible corrupt data received").get());
|
|
||||||
NS_WARNING("nsLDAPConnection::Run(): ldaperrno = "
|
|
||||||
"LDAP_DECODING_ERROR after ldap_result()");
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case NS_ERROR_OUT_OF_MEMORY:
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: ERROR: couldn't allocate memory for new LDAP message; search entry dropped").get());
|
|
||||||
// punt and hope things work out better next time around
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case NS_ERROR_ILLEGAL_VALUE:
|
|
||||||
case NS_ERROR_UNEXPECTED:
|
|
||||||
default:
|
|
||||||
// shouldn't happen; internal error
|
|
||||||
//
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: DEBUG: nsLDAPConnection::Run(): nsLDAPMessage::Init() returned unexpected value").get());
|
|
||||||
NS_WARNING("nsLDAPConnection::Run(): nsLDAPMessage::Init() "
|
|
||||||
"returned unexpected value.");
|
|
||||||
|
|
||||||
// punt and hope things work out better next time around
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// now let the scoping mechanisms provided by nsCOMPtr manage
|
|
||||||
// the reference for us.
|
|
||||||
//
|
|
||||||
msg = rawMsg;
|
|
||||||
|
|
||||||
// invoke the callback on the nsILDAPOperation corresponding to
|
|
||||||
// this message
|
|
||||||
//
|
|
||||||
rv = InvokeMessageCallback(msgHandle, msg, operationFinished);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
consoleSvc->LogStringMessage(
|
|
||||||
NS_LITERAL_STRING("LDAP: ERROR: problem invoking message callback").get());
|
|
||||||
NS_ERROR("LDAP: ERROR: problem invoking message callback");
|
|
||||||
// punt and hope things work out better next time around
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// sleep for a while to workaround event queue flooding
|
|
||||||
// (bug 50104) so that it's possible to test cancelling, firing
|
|
||||||
// status, etc.
|
|
||||||
//
|
|
||||||
PR_Sleep(1000);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX figure out how to break out of the while() loop and get here to
|
|
||||||
// so we can expire (though not if DEBUG is defined, since gdb gets ill
|
|
||||||
// if threads exit
|
|
||||||
//
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle,
|
nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle,
|
||||||
nsILDAPMessage *aMsg,
|
nsILDAPMessage *aMsg,
|
||||||
|
@ -619,3 +513,259 @@ nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle,
|
||||||
delete key;
|
delete key;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
//
|
||||||
|
nsLDAPConnectionLoop::nsLDAPConnectionLoop()
|
||||||
|
: mWeakConn(0),
|
||||||
|
mLock(0)
|
||||||
|
{
|
||||||
|
NS_INIT_ISUPPORTS();
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
//
|
||||||
|
nsLDAPConnectionLoop::~nsLDAPConnectionLoop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsLDAPConnectionLoop, nsIRunnable);
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsLDAPConnectionLoop::Init()
|
||||||
|
{
|
||||||
|
if (!mLock) {
|
||||||
|
mLock = PR_NewLock();
|
||||||
|
if (!mLock) {
|
||||||
|
NS_ERROR("nsLDAPConnectionLoop::Init: out of memory ");
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// for nsIRunnable. this thread spins in ldap_result() awaiting the next
|
||||||
|
// message. once one arrives, it dispatches it to the nsILDAPMessageListener
|
||||||
|
// on the main thread.
|
||||||
|
//
|
||||||
|
// XXX do all returns from this function need to do thread cleanup?
|
||||||
|
//
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsLDAPConnectionLoop::Run(void)
|
||||||
|
{
|
||||||
|
int lderrno;
|
||||||
|
nsresult rv;
|
||||||
|
PRInt32 returnCode;
|
||||||
|
LDAPMessage *msgHandle;
|
||||||
|
nsCOMPtr<nsILDAPMessage> msg;
|
||||||
|
struct timeval timeout = { 1, 0 };
|
||||||
|
PRIntervalTime sleepTime = PR_MillisecondsToInterval(10);
|
||||||
|
|
||||||
|
PR_LOG(gLDAPLogModule, PR_LOG_DEBUG,
|
||||||
|
("nsLDAPConnection::Run() entered\n"));
|
||||||
|
|
||||||
|
// get the console service so we can log messages
|
||||||
|
//
|
||||||
|
nsCOMPtr<nsIConsoleService> consoleSvc =
|
||||||
|
do_GetService(kConsoleServiceContractId, &rv);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_ERROR("nsLDAPConnection::Run() couldn't get console service");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the thread-specific data for the child thread (as necessary)
|
||||||
|
//
|
||||||
|
if (!nsLDAPThreadDataInit()) {
|
||||||
|
NS_ERROR("nsLDAPConnection::Run() couldn't initialize "
|
||||||
|
"thread-specific data");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for results
|
||||||
|
//
|
||||||
|
while(1) {
|
||||||
|
PRBool operationFinished = PR_TRUE;
|
||||||
|
nsCOMPtr<nsILDAPConnection> conn;
|
||||||
|
|
||||||
|
// Exit this thread if we no longer have an nsLDAPConnection
|
||||||
|
// associcated with it. We also aquire a lock here, to make sure
|
||||||
|
// to avoid a possible race condition when the nsLDAPConnection
|
||||||
|
// is destructed during the call to do_QueryReferent() (since
|
||||||
|
// that function isn't MT safe).
|
||||||
|
//
|
||||||
|
PR_Lock(mLock);
|
||||||
|
conn = do_QueryReferent(mWeakConn);
|
||||||
|
PR_Unlock(mLock);
|
||||||
|
|
||||||
|
if (!conn) {
|
||||||
|
mWeakConn = 0;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsLDAPConnection *rawConn = NS_STATIC_CAST(nsLDAPConnection *,
|
||||||
|
NS_STATIC_CAST(nsILDAPConnection *, conn));
|
||||||
|
|
||||||
|
// in case something went wrong on the last iteration, be sure to
|
||||||
|
// cause nsCOMPtr to release the message before going to sleep in
|
||||||
|
// ldap_result
|
||||||
|
//
|
||||||
|
msg = 0;
|
||||||
|
|
||||||
|
// XXX deal with timeouts better
|
||||||
|
//
|
||||||
|
// returnCode = ldap_result(rawConn->mConnectionHandle,
|
||||||
|
returnCode = ldap_result(rawConn->mConnectionHandle,
|
||||||
|
LDAP_RES_ANY, LDAP_MSG_ONE,
|
||||||
|
&timeout, &msgHandle);
|
||||||
|
|
||||||
|
// if we didn't error or timeout, create an nsILDAPMessage
|
||||||
|
//
|
||||||
|
switch (returnCode) {
|
||||||
|
|
||||||
|
case 0: // timeout
|
||||||
|
|
||||||
|
// the connection may not exist yet. sleep for a while
|
||||||
|
// and try again
|
||||||
|
//
|
||||||
|
PR_LOG(gLDAPLogModule, PR_LOG_WARNING,
|
||||||
|
("ldap_result() timed out.\n"));
|
||||||
|
conn = 0;
|
||||||
|
|
||||||
|
// The sleep here is to avoid a problem where the LDAP
|
||||||
|
// Connection/thread isn't ready quite yet, and we want to
|
||||||
|
// avoid a very busy loop.
|
||||||
|
//
|
||||||
|
PR_Sleep(sleepTime);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case -1: // something went wrong
|
||||||
|
|
||||||
|
lderrno = ldap_get_lderrno(rawConn->mConnectionHandle, 0, 0);
|
||||||
|
|
||||||
|
switch (lderrno) {
|
||||||
|
|
||||||
|
case LDAP_SERVER_DOWN:
|
||||||
|
// XXXreconnect or fail ?
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LDAP_DECODING_ERROR:
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: WARNING: decoding error; possible corrupt data received").get());
|
||||||
|
NS_WARNING("nsLDAPConnection::Run(): ldaperrno = "
|
||||||
|
"LDAP_DECODING_ERROR after ldap_result()");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LDAP_NO_MEMORY:
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: ERROR: couldn't allocate memory while getting async operation result").get());
|
||||||
|
// punt and hope things work out better next time around
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// shouldn't happen; internal error
|
||||||
|
//
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: DEBUG: ldaperrno set to unexpected value after ldap_result() call in nsLDAPConnection::Run()").get());
|
||||||
|
NS_WARNING("nsLDAPConnection::Run(): ldaperrno set to "
|
||||||
|
"unexpected value after ldap_result() "
|
||||||
|
"call in nsLDAPConnection::Run()");
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LDAP_RES_SEARCH_ENTRY:
|
||||||
|
case LDAP_RES_SEARCH_REFERENCE:
|
||||||
|
// XXX what should we do with LDAP_RES_SEARCH_EXTENDED?
|
||||||
|
|
||||||
|
// not done yet, so we shouldn't remove the op from the conn q
|
||||||
|
operationFinished = PR_FALSE;
|
||||||
|
|
||||||
|
// fall through to default case
|
||||||
|
|
||||||
|
default: // initialize the message and call the callback
|
||||||
|
|
||||||
|
// we want nsLDAPMessage specifically, not a compatible, since
|
||||||
|
// we're sharing native objects used by the LDAP C SDK
|
||||||
|
//
|
||||||
|
nsLDAPMessage *rawMsg = new nsLDAPMessage();
|
||||||
|
if (!rawMsg) {
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: ERROR: couldn't allocate memory for new LDAP message; search entry dropped").get());
|
||||||
|
// punt and hope things work out better next time around
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the message, using a protected method not available
|
||||||
|
// through nsILDAPMessage (which is why we need the raw pointer)
|
||||||
|
//
|
||||||
|
rv = rawMsg->Init(conn, msgHandle);
|
||||||
|
|
||||||
|
switch (rv) {
|
||||||
|
|
||||||
|
case NS_OK:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NS_ERROR_LDAP_DECODING_ERROR:
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: WARNING: decoding error; possible corrupt data received").get());
|
||||||
|
NS_WARNING("nsLDAPConnection::Run(): ldaperrno = "
|
||||||
|
"LDAP_DECODING_ERROR after ldap_result()");
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case NS_ERROR_OUT_OF_MEMORY:
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: ERROR: couldn't allocate memory for new LDAP message; search entry dropped").get());
|
||||||
|
// punt and hope things work out better next time around
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case NS_ERROR_ILLEGAL_VALUE:
|
||||||
|
case NS_ERROR_UNEXPECTED:
|
||||||
|
default:
|
||||||
|
// shouldn't happen; internal error
|
||||||
|
//
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: DEBUG: nsLDAPConnection::Run(): nsLDAPMessage::Init() returned unexpected value").get());
|
||||||
|
NS_WARNING("nsLDAPConnection::Run(): nsLDAPMessage::Init() "
|
||||||
|
"returned unexpected value.");
|
||||||
|
|
||||||
|
// punt and hope things work out better next time around
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now let the scoping mechanisms provided by nsCOMPtr manage
|
||||||
|
// the reference for us.
|
||||||
|
//
|
||||||
|
msg = rawMsg;
|
||||||
|
|
||||||
|
// invoke the callback on the nsILDAPOperation corresponding to
|
||||||
|
// this message
|
||||||
|
//
|
||||||
|
rv = rawConn->InvokeMessageCallback(msgHandle, msg,
|
||||||
|
operationFinished);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
consoleSvc->LogStringMessage(
|
||||||
|
NS_LITERAL_STRING("LDAP: ERROR: problem invoking message callback").get());
|
||||||
|
NS_ERROR("LDAP: ERROR: problem invoking message callback");
|
||||||
|
// punt and hope things work out better next time around
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// sleep for a while to workaround event queue flooding
|
||||||
|
// (bug 50104) so that it's possible to test cancelling, firing
|
||||||
|
// status, etc.
|
||||||
|
//
|
||||||
|
PR_Sleep(1000);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will never happen, but here just in case.
|
||||||
|
//
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
* Rights Reserved.
|
* Rights Reserved.
|
||||||
*
|
*
|
||||||
* Contributor(s): Dan Mosedale <dmose@mozilla.org>
|
* Contributor(s): Dan Mosedale <dmose@mozilla.org>
|
||||||
|
* Leif Hedstrom <leif@netscape.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the
|
* Alternatively, the contents of this file may be used under the
|
||||||
* terms of the GNU General Public License Version 2 or later (the
|
* terms of the GNU General Public License Version 2 or later (the
|
||||||
|
@ -44,6 +45,9 @@
|
||||||
#include "nsILDAPMessageListener.h"
|
#include "nsILDAPMessageListener.h"
|
||||||
#include "nsHashtable.h"
|
#include "nsHashtable.h"
|
||||||
#include "nspr.h"
|
#include "nspr.h"
|
||||||
|
#include "nsWeakReference.h"
|
||||||
|
#include "nsWeakPtr.h"
|
||||||
|
|
||||||
|
|
||||||
// 0d871e30-1dd2-11b2-8ea9-831778c78e93
|
// 0d871e30-1dd2-11b2-8ea9-831778c78e93
|
||||||
//
|
//
|
||||||
|
@ -51,14 +55,15 @@
|
||||||
{ 0x0d871e30, 0x1dd2, 0x11b2, \
|
{ 0x0d871e30, 0x1dd2, 0x11b2, \
|
||||||
{ 0x8e, 0xa9, 0x83, 0x17, 0x78, 0xc7, 0x8e, 0x93 }}
|
{ 0x8e, 0xa9, 0x83, 0x17, 0x78, 0xc7, 0x8e, 0x93 }}
|
||||||
|
|
||||||
class nsLDAPConnection : public nsILDAPConnection, nsIRunnable
|
class nsLDAPConnection : public nsILDAPConnection,
|
||||||
|
public nsSupportsWeakReference
|
||||||
{
|
{
|
||||||
friend class nsLDAPOperation;
|
friend class nsLDAPOperation;
|
||||||
friend class nsLDAPMessage;
|
friend class nsLDAPMessage;
|
||||||
|
friend class nsLDAPConnectionLoop;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSIRUNNABLE
|
|
||||||
NS_DECL_NSILDAPCONNECTION
|
NS_DECL_NSILDAPCONNECTION
|
||||||
|
|
||||||
// constructor & destructor
|
// constructor & destructor
|
||||||
|
@ -99,12 +104,39 @@ class nsLDAPConnection : public nsILDAPConnection, nsIRunnable
|
||||||
*/
|
*/
|
||||||
nsresult RemovePendingOperation(nsILDAPOperation *aOperation);
|
nsresult RemovePendingOperation(nsILDAPOperation *aOperation);
|
||||||
|
|
||||||
|
|
||||||
LDAP *mConnectionHandle; // the LDAP C SDK's connection object
|
LDAP *mConnectionHandle; // the LDAP C SDK's connection object
|
||||||
nsString *mBindName; // who to bind as
|
nsString *mBindName; // who to bind as
|
||||||
nsCOMPtr<nsIThread> mThread; // thread which marshals results
|
nsCOMPtr<nsIThread> mThread; // thread which marshals results
|
||||||
|
|
||||||
nsSupportsHashtable *mPendingOperations; // keep these around for callbacks
|
nsSupportsHashtable *mPendingOperations; // keep these around for callbacks
|
||||||
|
nsLDAPConnectionLoop *mRunnable; // nsIRunnable object
|
||||||
|
};
|
||||||
|
|
||||||
|
// This class implements the nsIRunnable interface, in this case just a
|
||||||
|
// Run() method. This is to be used within the nsLDAPConnection only, when
|
||||||
|
// creating a new thread.
|
||||||
|
//
|
||||||
|
class nsLDAPConnectionLoop : public nsIRunnable
|
||||||
|
{
|
||||||
|
friend class nsLDAPConnection;
|
||||||
|
friend class nsLDAPMessage;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
|
||||||
|
// constructor & destructor
|
||||||
|
//
|
||||||
|
nsLDAPConnectionLoop();
|
||||||
|
virtual ~nsLDAPConnectionLoop();
|
||||||
|
|
||||||
|
NS_IMETHOD Init();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
nsWeakPtr mWeakConn; // the connection object, a weak reference
|
||||||
|
PRLock *mLock; // Lock mechanism, since weak references
|
||||||
|
// aren't thread safe
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _nsLDAPConnection_h_
|
#endif // _nsLDAPConnection_h_
|
||||||
|
|
|
@ -50,6 +50,7 @@ class nsLDAPMessage : public nsILDAPMessage
|
||||||
{
|
{
|
||||||
friend class nsLDAPOperation;
|
friend class nsLDAPOperation;
|
||||||
friend class nsLDAPConnection;
|
friend class nsLDAPConnection;
|
||||||
|
friend class nsLDAPConnectionLoop;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче