diff -r bbf8bed59bcb -r 2bb754abd467 kernel/eka/kernel/sproperty.cpp --- a/kernel/eka/kernel/sproperty.cpp Wed Jun 09 11:10:19 2010 +0300 +++ b/kernel/eka/kernel/sproperty.cpp Mon Jun 21 17:12:14 2010 +0300 @@ -29,6 +29,14 @@ return TheCurrentThread->iOwningProcess; } +// Used by a thread to schedule cancelation of Subscribe request in Supervisor thread. +class TCancelQ: public SDblQueLink +{ +public: + TPropertySubsRequest* iPropSubRequest;//The request to be cancelled, can be NULL + NFastSemaphore iFSemaphore;//Semafore to be signalled by Supervisor thread after the request is cancelled. +}; + class TProperty { public: @@ -112,7 +120,10 @@ static void CompleteRequest(TPropertySubsRequest*, TInt aReason); static void CompleteQue(SDblQue* aQue, TInt aReason); static void CompleteDfc(TAny* aQue); - + static void CompleteDfcByKErrPermissionDenied(TAny* aQue); + static void CompleteDfcByKErrNotFound(TAny* aQue); + static void CompleteCancellationQDfc(TAny* aQue); + static TUint Hash(TUint aCategory, TUint aKey); static TProperty** Lookup(TUint aCategory, TUint aKey); static TInt LookupOrCreate(TUint aCategory, TUint aKey, TProperty**); @@ -186,10 +197,17 @@ #endif // !__REMOVE_PLATSEC_DIAGNOSTIC_STRINGS__ - enum { KCompletionDfcPriority = 2 }; + enum { KCancellationDfcPriority = 1, KCompletionDfcPriority = 2 }; static TDfc CompletionDfc; - // subscriptions to be completed by the DFC - static SDblQue CompletionQue; // protected by the system lock + static TDfc CompletionDfcPermissionDenied; + static TDfc CompletionDfcNotFound; + static TDfc CancellationDfc; + + // subscriptions to be completed by the DFCs, protected by system lock + static SDblQue CompletionQue; // to be completed by KerrNone + static SDblQue CompletionQuePermissionDenied; + static SDblQue CompletionQueNotFound; + static SDblQue CancellationQue; static DMutex* FeatureLock; ///< Order KMutexOrdPubSub @@ -266,8 +284,20 @@ }; +// Completion/Cancelation DFCs and their corresponding queues. +// All subscribe requests are completed in Supervisor thread. TDfc TProperty::CompletionDfc(TProperty::CompleteDfc, &TProperty::CompletionQue, KCompletionDfcPriority); SDblQue TProperty::CompletionQue; + +TDfc TProperty::CompletionDfcPermissionDenied(TProperty::CompleteDfcByKErrPermissionDenied, &TProperty::CompletionQuePermissionDenied, KCompletionDfcPriority); +SDblQue TProperty::CompletionQuePermissionDenied; + +TDfc TProperty::CompletionDfcNotFound(TProperty::CompleteDfcByKErrNotFound, &TProperty::CompletionQueNotFound, KCompletionDfcPriority); +SDblQue TProperty::CompletionQueNotFound; + +TDfc TProperty::CancellationDfc(TProperty::CompleteCancellationQDfc, &TProperty::CancellationQue, KCancellationDfcPriority); +SDblQue TProperty::CancellationQue; + DMutex* TProperty::FeatureLock; TProperty* TProperty::Table[KHashTableLimit]; @@ -288,6 +318,9 @@ if (r != KErrNone) return r; CompletionDfc.SetDfcQ(K::SvMsgQ); + CompletionDfcPermissionDenied.SetDfcQ(K::SvMsgQ); + CompletionDfcNotFound.SetDfcQ(K::SvMsgQ); + CancellationDfc.SetDfcQ(K::SvMsgQ); #ifdef __DEMAND_PAGING__ r = Kern::MutexCreate(PagingLockMutex, KPubSubMutexName2, KMutexOrdPubSub2); @@ -512,8 +545,9 @@ // iteration; we get (move) always the first 'iPendingQue' entry until the // queue becomes empty. // - SDblQue localPendingQue; // protected by the system lock - SDblQue localCompletionQue; // protected by the system lock + + SDblQue localPendingQue; // Will hold requests with sufficient capabilities. + TInt accessDeniedCounter = 0;// Will count requests with no sufficient capabilities. NKern::LockSystem(); while (!iPendingQue.IsEmpty()) { @@ -522,7 +556,8 @@ subs->iProcess = NULL; if (process && !CheckGetRights(process, __PLATSEC_DIAGNOSTIC_STRING("Checked whilst trying to Subscribe to a Publish and Subscribe Property"))) { // Check fails - will complete the subscription with an error - localCompletionQue.Add(subs); + CompletionQuePermissionDenied.Add(subs); + accessDeniedCounter++; } else { // Check OK - will leave in the pending queue @@ -536,8 +571,10 @@ // Now the property can be accessed by other threads. Use(); Unlock(); - // Now we can complete requests. - CompleteQue(&localCompletionQue, KErrPermissionDenied); + + // Schedule DFC to complete requests of those with insufficient capabilities. + if (accessDeniedCounter) + CompletionDfcPermissionDenied.Enque(); return KErrNone; } @@ -604,9 +641,10 @@ SetNotDefined(); // Down from here nobody can access the property value - // We don't want to complete requests holding the feature lock. - SDblQue localQue; - localQue.MoveFrom(&iPendingQue); + // Move all pending requests to completion queue (to be completed by KErrNotFound). + TBool pendingQueEmpty = iPendingQue.IsEmpty(); + if(!pendingQueEmpty) + CompletionQueNotFound.MoveFrom(&iPendingQue); NKern::UnlockSystem(); @@ -616,8 +654,10 @@ Release(); // '*this' may do not exist any more Unlock(); - // Now we can complete. - CompleteQue(&localQue, KErrNotFound); + + // Schedule Svc Dfc to complete all requests from CompletionQueNotFound. + if (!pendingQueEmpty) + CompletionDfcNotFound.Enque(); return KErrNone; } @@ -661,9 +701,8 @@ aSubs->iProcess = NULL; if (aProcess && !CheckGetRights(aProcess,__PLATSEC_DIAGNOSTIC_STRING("Checked whilst trying to Subscribe to a Publish and Subscribe Property"))) { - NKern::ThreadEnterCS(); - CompleteRequest(aSubs, KErrPermissionDenied); - NKern::ThreadLeaveCS(); + CompletionQuePermissionDenied.Add(aSubs); + CompletionDfcPermissionDenied.Enque(SYSTEM_LOCK); return KErrNone; } } @@ -681,36 +720,70 @@ void TProperty::Cancel(TPropertySubsRequest* aSubs) { __ASSERT_SYSTEM_LOCK; - if (!aSubs->iNext) - { // not pending - silently return - NKern::UnlockSystem(); + + // This is set if the request is about to be completed (in SVC thread). In that case, a 'dummy' CancelationDFC + // will be scheduled here. This will just ensure that we don't return from Cancel before ongoing completion has finished. + TBool scheduledForCompletition = (TInt)(aSubs->iProcess) & TPropertySubsRequest::KScheduledForCompletion; + + if (!aSubs->iNext && !scheduledForCompletition) + { // Neither in any queue nor scheduled for completion. + // The request is not active - silently return. + NKern::UnlockSystem(); return; } - aSubs->Deque(); - aSubs->iNext = NULL; - aSubs->iProcess = NULL; - NKern::ThreadEnterCS(); - CompleteRequest(aSubs, KErrCancel); - NKern::ThreadLeaveCS(); + + if (aSubs->iNext) + { + // Take it out from the current queue. It is usually pending queue of a property but could + // also be one of the completion queues. + aSubs->Deque(); + aSubs->iNext = NULL; + } + + // Set process to NULL (leave KScheduledForCompletion bit as it is) + aSubs->iProcess = (DProcess*)((TInt)aSubs->iProcess & ~TPropertySubsRequest::KProcessPtrMask); + + if (&Kern::CurrentThread() == K::SvThread) + { // Complete the request immediatelly if already running in supervisor thread... + if (!scheduledForCompletition) + CompleteRequest(aSubs, KErrCancel); //This will also release system lock. + else + NKern::UnlockSystem(); // Nothing to be done here. Just release system lock. + } + else + { //... or schedule DFC in supervisor thread to complete the request. + TCancelQ linkQ; + if (!scheduledForCompletition) + linkQ.iPropSubRequest = aSubs; // CancelationDFC will complete this request with KErrCancel. + else + linkQ.iPropSubRequest = NULL; // Schedule 'dummy' CancelationDFC (no request will be completed). + linkQ.iFSemaphore.iOwningThread = NKern::CurrentThread(); + CancellationQue.Add(&linkQ); + CancellationDfc.Enque(SYSTEM_LOCK); // This will also release system lock. + + NKern::FSWait(&linkQ.iFSemaphore); // Wait for CancellationDfc to finish. + } } // Enter system locked. // Return system unlocked. -// Called in CS or a DFC context +// Executed in supervisor thread. void TProperty::CompleteRequest(TPropertySubsRequest* aSubs, TInt aResult) { // static __ASSERT_SYSTEM_LOCK; - __ASSERT_CRITICAL; + __PS_ASSERT(&Kern::CurrentThread() == K::SvThread); TPropertyCompleteFn fn = aSubs->iCompleteFn; TAny* ptr = aSubs->iPtr; + // Mark that this request is about to be completed. + aSubs->iProcess = (DProcess*)((TInt)aSubs->iProcess | TPropertySubsRequest::KScheduledForCompletion); NKern::UnlockSystem(); (*fn)(ptr, aResult); } -// Called in CS or a DFC context +// Executed in supervisor thread. void TProperty::CompleteQue(SDblQue* aQue, TInt aReason) { // static - __ASSERT_CRITICAL; + __PS_ASSERT(&Kern::CurrentThread() == K::SvThread); NKern::LockSystem(); while (!aQue->IsEmpty()) { @@ -723,12 +796,53 @@ NKern::UnlockSystem(); } -// Executed in DFC context +// Executed in supervisor thread. Completes requests with KErrNone. void TProperty::CompleteDfc(TAny* aQue) { // static CompleteQue((SDblQue*) aQue, KErrNone); } +// Executed in supervisor thread. +void TProperty::CompleteDfcByKErrPermissionDenied(TAny* aQue) + { // static + CompleteQue((SDblQue*) aQue, KErrPermissionDenied); + } + +// Executed in supervisor thread. +void TProperty::CompleteDfcByKErrNotFound(TAny* aQue) + { // static + CompleteQue((SDblQue*) aQue, KErrNotFound); + } + +// Executed in supervisor thread. +void TProperty::CompleteCancellationQDfc(TAny* aAny) + { // static + SDblQue* aQue = (SDblQue*)aAny; + NKern::LockSystem(); + while (!aQue->IsEmpty() ) + { + TCancelQ* first = static_cast(aQue->First()); + first->Deque(); + first->iNext = NULL; + + if( first->iPropSubRequest) + { + CompleteRequest(first->iPropSubRequest, KErrCancel); // This will also release system lock. + NKern::LockSystem(); + } + else + { + // Do not complete the request. + // It was already just about to be completed when Cancel request was issued. + // As all complitions (this method included) run is Svc thread, we can here be sure that + // the request is now completed. + NKern::FlashSystem(); // Preemption point + } + NKern::FSSignal( &first->iFSemaphore ); // Issue the signal that the request is now completed. + } + NKern::UnlockSystem(); + } + // Enter system locked. // Return system unlocked. void TProperty::CompleteByDfc() @@ -900,7 +1014,7 @@ return KErrArgument; } iValue = aValue; - CompleteByDfc(); + CompleteByDfc(); //This will also release system lock. return KErrNone; }