os/kernelhwsrv/userlibandfileserver/fileserver/sfile/sf_thread.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of the License "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
// f32\sfile\sf_thread.cpp
sl@0
    15
// 
sl@0
    16
//
sl@0
    17
sl@0
    18
#include "sf_std.h"
sl@0
    19
#include <u32exec.h>
sl@0
    20
#include "sf_file_cache.h"
sl@0
    21
sl@0
    22
#define __CHECK_DRVNUM(d) {__ASSERT_DEBUG(d>=EDriveA && d<=EDriveZ,Fault(EFsThreadBadDrvNum));}
sl@0
    23
sl@0
    24
#ifdef __X86__
sl@0
    25
const TInt KRequestThreadStackSize = 0x4000;
sl@0
    26
#else
sl@0
    27
const TInt KRequestThreadStackSize = 0x3000;
sl@0
    28
#endif
sl@0
    29
sl@0
    30
const TInt KFinaliseTimerPeriod = 10 * 1000 * 1000;	// default 10S finalisation timeout
sl@0
    31
sl@0
    32
TFsDriveThread FsThreadManager::iFsThreads[KMaxDrives];
sl@0
    33
TUint FsThreadManager::iMainId=0;
sl@0
    34
CDisconnectThread* FsThreadManager::iDisconnectThread=NULL;
sl@0
    35
TUint FsThreadManager::iDisconnectThreadId=0;
sl@0
    36
sl@0
    37
TFsDriveThread::TFsDriveThread()
sl@0
    38
//
sl@0
    39
//
sl@0
    40
//
sl@0
    41
:iIsAvailable(EFalse),iIsSync(EFalse),iThread(NULL),iId(0),iIsHung(EFalse),iMediaChangePending(EFalse)
sl@0
    42
	{
sl@0
    43
	TInt r=iFSLock.CreateLocal();
sl@0
    44
	__ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor));
sl@0
    45
	}
sl@0
    46
sl@0
    47
TFsPluginThread::TFsPluginThread()
sl@0
    48
//
sl@0
    49
//
sl@0
    50
//
sl@0
    51
:iIsAvailable(ETrue),iThread(NULL),iId(0)
sl@0
    52
	{
sl@0
    53
	TInt r=iPluginLock.CreateLocal();
sl@0
    54
	iDriveNumber= KMaxDrives+1;
sl@0
    55
	__ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor));
sl@0
    56
	}
sl@0
    57
sl@0
    58
TInt FsThreadManager::CreateDisconnectThread()
sl@0
    59
//
sl@0
    60
// Called just once at startup
sl@0
    61
//
sl@0
    62
	{
sl@0
    63
	__PRINT(_L("Create disconnect thread"));
sl@0
    64
	TRAPD(r,iDisconnectThread=CDisconnectThread::NewL());
sl@0
    65
	if(r!=KErrNone)
sl@0
    66
		return(r);
sl@0
    67
	TRAP(r,iDisconnectThreadId=iDisconnectThread->StartL());
sl@0
    68
	if(r!=KErrNone)
sl@0
    69
		{
sl@0
    70
		delete(iDisconnectThread);
sl@0
    71
		iDisconnectThread=NULL;
sl@0
    72
		iDisconnectThreadId=0;
sl@0
    73
		}
sl@0
    74
	__THRD_PRINT2(_L("iDisconnectThread=0x%x id=0x%x"),iDisconnectThread,iDisconnectThreadId);
sl@0
    75
	return(r);
sl@0
    76
	}
sl@0
    77
sl@0
    78
TBool FsThreadManager::IsDisconnectThread()
sl@0
    79
//
sl@0
    80
// Return ETrue if the calling thread is the disconnect thread
sl@0
    81
//
sl@0
    82
	{
sl@0
    83
	return(iDisconnectThreadId==RThread().Id());
sl@0
    84
	}
sl@0
    85
sl@0
    86
sl@0
    87
TInt FsThreadManager::InitDrive(TInt aDrvNumber,TBool aIsSync)
sl@0
    88
//
sl@0
    89
// Create a drive thread
sl@0
    90
// Should only by called from main file server thread with drive thread unavailable
sl@0
    91
// 
sl@0
    92
//
sl@0
    93
	{
sl@0
    94
	__PRINT1(_L("FsThreadManager::InitDrive() drive=%d"),aDrvNumber);
sl@0
    95
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
    96
	__ASSERT_ALWAYS(!t.iIsAvailable,Fault(EThreadManagerInitDrive));
sl@0
    97
	t.iIsSync=ETrue;
sl@0
    98
	
sl@0
    99
	return ChangeSync(aDrvNumber, aIsSync);
sl@0
   100
	}
sl@0
   101
sl@0
   102
sl@0
   103
TInt FsThreadManager::ChangeSync(TInt aDrvNumber,TBool aIsSync)
sl@0
   104
//
sl@0
   105
// Change if a drive is syncronouse after it has been inishalised.
sl@0
   106
// Should be called from the main thread.  
sl@0
   107
// Any pending oporations will be compleated.
sl@0
   108
//
sl@0
   109
sl@0
   110
	{
sl@0
   111
	__PRINT1(_L("FsThreadManager::ChangeSync() drive=%d"),aDrvNumber);
sl@0
   112
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   113
	__CHECK_MAINTHREAD();
sl@0
   114
sl@0
   115
	LockDrive(aDrvNumber);
sl@0
   116
	TFsDriveThread& t=FsThreadManager::GetFsDriveThread(aDrvNumber);
sl@0
   117
	TInt r=KErrNone;
sl@0
   118
sl@0
   119
	if (aIsSync!=t.iIsSync)
sl@0
   120
		{
sl@0
   121
		if (!aIsSync)
sl@0
   122
			{
sl@0
   123
			if(!t.iThread)
sl@0
   124
				{
sl@0
   125
				TRAP(r,t.iThread=CDriveThread::NewL());
sl@0
   126
				if(r!=KErrNone)
sl@0
   127
					{
sl@0
   128
					UnlockDrive(aDrvNumber);
sl@0
   129
					return(r);
sl@0
   130
					}
sl@0
   131
				}
sl@0
   132
			TRAP(r,t.iId=t.iThread->StartL(aDrvNumber));
sl@0
   133
			__THRD_PRINT2(_L("Starting thread 0x%x returned %d"),&t,r);
sl@0
   134
			if(r!=KErrNone)
sl@0
   135
				aIsSync=ETrue;
sl@0
   136
			else
sl@0
   137
				{
sl@0
   138
				t.iIsSync=EFalse;
sl@0
   139
				__THRD_PRINT1(_L("drive thread id=0x%x"),t.iId);
sl@0
   140
				}
sl@0
   141
			}
sl@0
   142
		if (aIsSync)
sl@0
   143
			{
sl@0
   144
			if (t.iThread)
sl@0
   145
				{
sl@0
   146
				t.iThread->CompleteAllRequests(KErrNotReady);
sl@0
   147
				t.iThread->iExit=ETrue;
sl@0
   148
				t.iThread=NULL;
sl@0
   149
				}
sl@0
   150
			t.iIsSync=ETrue;
sl@0
   151
			}
sl@0
   152
		}
sl@0
   153
	if (r==KErrNone)
sl@0
   154
		t.iIsAvailable=ETrue;
sl@0
   155
	
sl@0
   156
	UnlockDrive(aDrvNumber);	
sl@0
   157
	return r;
sl@0
   158
	}
sl@0
   159
sl@0
   160
void FsThreadManager::CloseDrive(TInt aDrvNumber)
sl@0
   161
//
sl@0
   162
// Close a drive thread
sl@0
   163
// Assumes already locked or safe
sl@0
   164
// If file system in not synchronous then should be called from a drive thread request
sl@0
   165
//
sl@0
   166
	{
sl@0
   167
	__PRINT1(_L("FsThreadManager::CloseDrive() drive=%d"),aDrvNumber);
sl@0
   168
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   169
sl@0
   170
	// no need to cancel requests if synchronous since queued
sl@0
   171
	if(!FsThreadManager::IsDriveSync(aDrvNumber,EFalse))
sl@0
   172
		{
sl@0
   173
		CDriveThread* pT=NULL;
sl@0
   174
		TInt r=FsThreadManager::GetDriveThread(aDrvNumber,&pT);
sl@0
   175
		__ASSERT_ALWAYS(r==KErrNone && pT,Fault(EDismountFsDriveThread));
sl@0
   176
		pT->CompleteAllRequests(KErrNotReady);
sl@0
   177
		}
sl@0
   178
sl@0
   179
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   180
	__ASSERT_ALWAYS(t.iIsAvailable,Fault(EFsThreadDriveClose1));
sl@0
   181
	if(!t.iIsSync)
sl@0
   182
		{
sl@0
   183
		__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(aDrvNumber,EFalse),Fault(EFsThreadDriveClose2));
sl@0
   184
		
sl@0
   185
		StopFinalisationTimer(aDrvNumber);
sl@0
   186
sl@0
   187
		// drive thread will exit when request completed
sl@0
   188
		t.iThread->iExit=ETrue;
sl@0
   189
sl@0
   190
		// Ensure that subsequent remounts use a new thread AND a new CDriveThread object - 
sl@0
   191
		// re-use of the CDriveThread object can lead to deadlock while both old & new threads are active.
sl@0
   192
		t.iThread = NULL;	
sl@0
   193
sl@0
   194
		// Empty the closed file queue for this drive before the thread ends and the CDriveThread object 
sl@0
   195
		// is deleted because the closed file queue contains a list of CFileCache objects which will 
sl@0
   196
		// call CRequestThread::RemoveTimer() when closed
sl@0
   197
		TClosedFileUtils::Remove(aDrvNumber);
sl@0
   198
		}
sl@0
   199
	else
sl@0
   200
		{
sl@0
   201
		__CHECK_MAINTHREAD();
sl@0
   202
		t.iIsSync=EFalse;
sl@0
   203
		}
sl@0
   204
	t.iIsAvailable=EFalse;
sl@0
   205
	t.iId=0;
sl@0
   206
	}
sl@0
   207
sl@0
   208
sl@0
   209
TBool FsThreadManager::IsDriveAvailable(TInt aDrvNumber,TBool aIsLock)
sl@0
   210
//
sl@0
   211
//
sl@0
   212
//
sl@0
   213
	{
sl@0
   214
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   215
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   216
	if(aIsLock)
sl@0
   217
		t.iFSLock.Wait();
sl@0
   218
	TBool b=t.iIsAvailable;
sl@0
   219
	if(aIsLock)
sl@0
   220
		t.iFSLock.Signal();
sl@0
   221
	__THRD_PRINT2(_L("drive thread %d iIsAvailable=%d"),aDrvNumber,b);
sl@0
   222
	return(b);
sl@0
   223
	}
sl@0
   224
sl@0
   225
TBool FsThreadManager::IsDriveSync(TInt aDrvNumber,TBool aLock)
sl@0
   226
//
sl@0
   227
// 
sl@0
   228
//
sl@0
   229
	{
sl@0
   230
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   231
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   232
	if(aLock)
sl@0
   233
		t.iFSLock.Wait();
sl@0
   234
	TBool b=(t.iIsAvailable&&t.iIsSync);
sl@0
   235
	if(aLock)
sl@0
   236
		t.iFSLock.Signal();
sl@0
   237
	__THRD_PRINT2(_L("drive thread %d iIsSync=%d"),aDrvNumber,b);
sl@0
   238
	return(b);
sl@0
   239
	}
sl@0
   240
sl@0
   241
TInt FsThreadManager::GetDriveThread(TInt aDrvNumber, CDriveThread** aDrvThread)
sl@0
   242
//
sl@0
   243
// Assumes locked or called from the drive thread
sl@0
   244
//
sl@0
   245
	{
sl@0
   246
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   247
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   248
	*aDrvThread=NULL;
sl@0
   249
	TInt r=KErrNone;
sl@0
   250
	if(!t.iIsAvailable)
sl@0
   251
		r=KErrNotReady;
sl@0
   252
	else if(t.iIsSync)
sl@0
   253
		r=KErrAccessDenied;
sl@0
   254
	else
sl@0
   255
		{
sl@0
   256
		*aDrvThread=t.iThread;
sl@0
   257
		__ASSERT_DEBUG(*aDrvThread,Fault(EFsThreadGetThread));
sl@0
   258
		}
sl@0
   259
	__THRD_PRINT4(_L("GetDriveThread(%d) r %d id=0x%x *aDrvThread=0x%x"),aDrvNumber, r, t.iId, *aDrvThread);
sl@0
   260
	return r;
sl@0
   261
	}
sl@0
   262
sl@0
   263
sl@0
   264
void FsThreadManager::LockDrive(TInt aDrvNumber)
sl@0
   265
//
sl@0
   266
// Lock the TFsDriveThread object for the aDrvNumber drive
sl@0
   267
//
sl@0
   268
	{
sl@0
   269
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   270
	__THRD_PRINT1(_L("FsThreadManager::LockDrive(%d)"),aDrvNumber);
sl@0
   271
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   272
	t.iFSLock.Wait();
sl@0
   273
	}
sl@0
   274
sl@0
   275
void FsThreadManager::UnlockDrive(TInt aDrvNumber)
sl@0
   276
//
sl@0
   277
// Unlock the TFsDriveThread object for the aDrvNumber drive
sl@0
   278
//
sl@0
   279
	{
sl@0
   280
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   281
	__THRD_PRINT1(_L("FsThreadManager::UnlockDrive(%d)"),aDrvNumber);
sl@0
   282
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   283
	t.iFSLock.Signal();
sl@0
   284
	}
sl@0
   285
sl@0
   286
void FsThreadManager::SetDriveHung(TInt aDrvNumber, TBool aIsHung)
sl@0
   287
	{
sl@0
   288
	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
sl@0
   289
		return;
sl@0
   290
sl@0
   291
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   292
sl@0
   293
	// quick exit if hung state not changing or drive thread not available
sl@0
   294
	if ((!t.iIsAvailable) || (t.iIsHung == aIsHung))
sl@0
   295
		return;
sl@0
   296
sl@0
   297
	t.iFSLock.Wait();
sl@0
   298
sl@0
   299
	// Don't clear the hung state if this is a synchronous request
sl@0
   300
	// and the drive is asynchronous - we need to wait for whatever 
sl@0
   301
	// asynchronous request caused the hang to complete first.
sl@0
   302
	TUint id=RThread().Id();
sl@0
   303
	TBool isDriveThread = t.iIsSync || (!t.iIsSync && t.iId == id);
sl@0
   304
	__THRD_PRINT3(_L("Set %d Hung %d. Is Drive thread %d"), aDrvNumber, aIsHung, isDriveThread);
sl@0
   305
	if (!aIsHung && !isDriveThread)
sl@0
   306
		{
sl@0
   307
		t.iFSLock.Signal();
sl@0
   308
		return;
sl@0
   309
		}
sl@0
   310
sl@0
   311
	t.iIsHung = aIsHung;
sl@0
   312
sl@0
   313
	// if we're no longer hung, see if there's a media change pending
sl@0
   314
	// and if so issue one now
sl@0
   315
	TBool mediaChangePending = EFalse;
sl@0
   316
	if(!aIsHung)
sl@0
   317
		{
sl@0
   318
		mediaChangePending = t.iMediaChangePending;
sl@0
   319
		t.iMediaChangePending = EFalse;
sl@0
   320
		}
sl@0
   321
	t.iFSLock.Signal();
sl@0
   322
sl@0
   323
	// If the drive is now hung we must complete all requests in the drive thread's
sl@0
   324
	// queue - and all subsequent requests - with KErrNotReady to prevent deadlock.
sl@0
   325
	// For example, the notifier server may try to access the loader but one of the
sl@0
   326
	// requests in the queue may already belong to the loader.
sl@0
   327
	if (aIsHung && t.iThread)
sl@0
   328
		t.iThread->CompleteClientRequests(KErrNotReady);
sl@0
   329
sl@0
   330
	if(mediaChangePending)
sl@0
   331
		FsNotify::DiskChange(aDrvNumber);
sl@0
   332
	}
sl@0
   333
sl@0
   334
sl@0
   335
TBool FsThreadManager::IsDriveHung(TInt aDrvNumber)
sl@0
   336
	{
sl@0
   337
	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
sl@0
   338
		return EFalse;
sl@0
   339
sl@0
   340
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   341
//	__THRD_PRINT3(_L("Is %d Hung = %d"), aDrvNumber, t.iIsHung);
sl@0
   342
	return t.iIsHung;
sl@0
   343
	}
sl@0
   344
sl@0
   345
sl@0
   346
// If the drive is hung, then don't complete any disk change 
sl@0
   347
// notifications until the request causing the hang completes.
sl@0
   348
void FsThreadManager::SetMediaChangePending(TInt aDrvNumber)
sl@0
   349
	{
sl@0
   350
	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
sl@0
   351
		return;
sl@0
   352
sl@0
   353
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   354
sl@0
   355
	if (!t.iIsAvailable)
sl@0
   356
		return;
sl@0
   357
sl@0
   358
	t.iFSLock.Wait();
sl@0
   359
	t.iMediaChangePending = ETrue;
sl@0
   360
	t.iFSLock.Signal();
sl@0
   361
	}
sl@0
   362
sl@0
   363
void FsThreadManager::SetMainThreadId()
sl@0
   364
//
sl@0
   365
// called at file server startup, assumes called from main file server thread
sl@0
   366
//
sl@0
   367
	{
sl@0
   368
	iMainId=RThread().Id();
sl@0
   369
	__THRD_PRINT1(_L("Main thread id = 0x%x"),iMainId);
sl@0
   370
	}
sl@0
   371
sl@0
   372
TBool FsThreadManager::IsDriveThread(TInt aDrvNumber,TBool aIsLock)
sl@0
   373
//
sl@0
   374
// Return ETrue if the calling thread is the aDrvNumber drive thread
sl@0
   375
//
sl@0
   376
	{
sl@0
   377
	__CHECK_DRVNUM(aDrvNumber);
sl@0
   378
	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
sl@0
   379
	TUint id=RThread().Id();
sl@0
   380
	if(aIsLock)
sl@0
   381
		t.iFSLock.Wait();
sl@0
   382
	TBool b = t.iIsAvailable && (!t.iIsSync && t.iId==id || t.iIsSync);
sl@0
   383
	if(aIsLock)
sl@0
   384
		t.iFSLock.Signal();
sl@0
   385
	return(b);
sl@0
   386
	}
sl@0
   387
	
sl@0
   388
TBool FsThreadManager::IsMainThread()
sl@0
   389
//
sl@0
   390
// Returns ETrue if calling thread is same as main file server thread
sl@0
   391
//
sl@0
   392
	{
sl@0
   393
	return((TUint)(RThread().Id())==iMainId);
sl@0
   394
	}
sl@0
   395
sl@0
   396
sl@0
   397
void FsThreadManager::StartFinalisationTimer(TInt aDrvNumber)
sl@0
   398
	{
sl@0
   399
	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
sl@0
   400
		return;
sl@0
   401
sl@0
   402
	// If the message could cause disk modification, make sure that the finalisation
sl@0
   403
	// timer is queued so that we can mark the disk consistent at some point in the future
sl@0
   404
	CDriveThread* driveThread=NULL;
sl@0
   405
	TInt r = GetDriveThread(aDrvNumber, &driveThread);
sl@0
   406
	if(r == KErrNone && driveThread != NULL)
sl@0
   407
		driveThread->StartFinalisationTimer();
sl@0
   408
	}
sl@0
   409
sl@0
   410
void FsThreadManager::StopFinalisationTimer(TInt aDrvNumber)
sl@0
   411
	{
sl@0
   412
	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
sl@0
   413
		return;
sl@0
   414
sl@0
   415
	// If the message could cause disk modification, make sure that the finalisation
sl@0
   416
	// timer is queued so that we can mark the disk consistent at some point in the future
sl@0
   417
	CDriveThread* dT=NULL;
sl@0
   418
	TInt r = GetDriveThread(aDrvNumber, &dT);
sl@0
   419
	if(r == KErrNone && dT != NULL)
sl@0
   420
		{
sl@0
   421
		dT->StopFinalisationTimer();
sl@0
   422
		}
sl@0
   423
	}
sl@0
   424
sl@0
   425
CRequestThread::CRequestThread()
sl@0
   426
//
sl@0
   427
//
sl@0
   428
//
sl@0
   429
:iList(_FOFF(CFsRequest,iLink))
sl@0
   430
	{
sl@0
   431
	//iRequest=NULL;
sl@0
   432
	//iIsWaiting=EFalse;
sl@0
   433
	iExit=EFalse;
sl@0
   434
	}
sl@0
   435
sl@0
   436
TInt CRequestThread::Initialise()
sl@0
   437
//
sl@0
   438
// Initialise
sl@0
   439
//
sl@0
   440
	{
sl@0
   441
	TInt r=iListLock.CreateLocal();
sl@0
   442
	return(r);
sl@0
   443
	}
sl@0
   444
sl@0
   445
CRequestThread::~CRequestThread()
sl@0
   446
//
sl@0
   447
//
sl@0
   448
//
sl@0
   449
	{
sl@0
   450
	__ASSERT_ALWAYS(iList.IsEmpty(),Fault(ERequestThreadDestructor));
sl@0
   451
	iListLock.Close();
sl@0
   452
sl@0
   453
	if(iThread.Handle() != 0)
sl@0
   454
		{
sl@0
   455
		iThread.Close();
sl@0
   456
		}
sl@0
   457
	delete iTimer;
sl@0
   458
	}
sl@0
   459
sl@0
   460
LOCAL_C TInt ThreadFunction(TAny* aPtr)
sl@0
   461
//
sl@0
   462
//
sl@0
   463
//
sl@0
   464
	{
sl@0
   465
	__THRD_PRINT(_L("ThreadFunction()"));
sl@0
   466
	User::SetCritical(User::ESystemCritical);
sl@0
   467
	CRequestThread* pT=(CRequestThread*)aPtr;
sl@0
   468
	TInt r = pT->ThreadFunction();
sl@0
   469
	delete pT;
sl@0
   470
	return r;
sl@0
   471
	}
sl@0
   472
sl@0
   473
void CRequestThread::CompleteAllRequests(TInt aValue)
sl@0
   474
    {
sl@0
   475
    __THRD_PRINT(_L("CRequestThread::CompleteAllRequests()"));
sl@0
   476
    iListLock.Wait();
sl@0
   477
    while(!iList.IsEmpty())
sl@0
   478
        {
sl@0
   479
        CFsRequest* pR=iList.First();
sl@0
   480
        pR->iLink.Deque();
sl@0
   481
        iListLock.Signal();
sl@0
   482
        pR->Complete(aValue);
sl@0
   483
        iListLock.Wait();
sl@0
   484
        }
sl@0
   485
    iListLock.Signal();
sl@0
   486
    __THRD_PRINT(_L("all requests completed"));
sl@0
   487
    }
sl@0
   488
sl@0
   489
TInt CRequestThread::ThreadFunction()
sl@0
   490
//
sl@0
   491
// entry point for the thread
sl@0
   492
//
sl@0
   493
	{
sl@0
   494
	iTimer = CFsDeltaTimer::New(*this, EPriorityLess);
sl@0
   495
	if (iTimer == NULL)
sl@0
   496
		{
sl@0
   497
		RThread::Rendezvous(KErrNoMemory);
sl@0
   498
		return(KErrNone);
sl@0
   499
		}
sl@0
   500
	iTimer->iStatus = KErrNotReady;
sl@0
   501
sl@0
   502
	CTrapCleanup* trapHandler=CTrapCleanup::New();
sl@0
   503
	if (trapHandler==NULL)
sl@0
   504
		{
sl@0
   505
		RThread::Rendezvous(KErrNoMemory);
sl@0
   506
		delete iTimer;
sl@0
   507
		return(KErrNone);
sl@0
   508
		}
sl@0
   509
sl@0
   510
	RThread::Rendezvous(KErrNone);
sl@0
   511
sl@0
   512
	TInt err = DoThreadInitialise();
sl@0
   513
	if(err != KErrNone)
sl@0
   514
		{
sl@0
   515
		delete trapHandler;
sl@0
   516
		return(KErrNone);
sl@0
   517
		}
sl@0
   518
sl@0
   519
	iExit=EFalse;
sl@0
   520
	iIsWaiting=EFalse;
sl@0
   521
	// start receiving
sl@0
   522
	Receive();
sl@0
   523
	CompleteAllRequests(KErrNotReady);
sl@0
   524
	
sl@0
   525
	delete trapHandler;
sl@0
   526
	return(KErrNone);
sl@0
   527
	}
sl@0
   528
sl@0
   529
TInt CRequestThread::DoThreadInitialise()
sl@0
   530
	{
sl@0
   531
	return KErrNone;
sl@0
   532
	}
sl@0
   533
sl@0
   534
TInt CRequestThread::DoStart(RThread& aThread)
sl@0
   535
//
sl@0
   536
// create thread and return handle
sl@0
   537
// necessary for client to close thread handle if successful
sl@0
   538
//
sl@0
   539
	{
sl@0
   540
	TInt r=aThread.Create(KNullDesC,::ThreadFunction,KRequestThreadStackSize,NULL,(TAny*)this);
sl@0
   541
	__PRINT1(_L("CRequestThread::DoStart() r=%d"),r);
sl@0
   542
	if(r!=KErrNone)
sl@0
   543
		return(r);
sl@0
   544
	TRequestStatus status;
sl@0
   545
	aThread.Rendezvous(status);
sl@0
   546
	if(status==KRequestPending)
sl@0
   547
		{
sl@0
   548
		aThread.SetPriority(EPriorityLess);
sl@0
   549
		aThread.Resume();
sl@0
   550
		}
sl@0
   551
	else
sl@0
   552
		{
sl@0
   553
		aThread.Kill(0);
sl@0
   554
		}
sl@0
   555
	User::WaitForRequest(status);
sl@0
   556
	r = status.Int();
sl@0
   557
	if(r!=KErrNone)
sl@0
   558
		aThread.Close();
sl@0
   559
	else
sl@0
   560
		iThread = aThread;
sl@0
   561
sl@0
   562
	return(r);
sl@0
   563
	}
sl@0
   564
	
sl@0
   565
sl@0
   566
void CRequestThread::Receive()
sl@0
   567
//
sl@0
   568
// Receive and process requests
sl@0
   569
//
sl@0
   570
	{
sl@0
   571
	FOREVER
sl@0
   572
		{
sl@0
   573
		iListLock.Wait();
sl@0
   574
		if(!iList.IsEmpty())
sl@0
   575
			{
sl@0
   576
			iRequest=iList.First();
sl@0
   577
			iRequest->iLink.Deque();
sl@0
   578
			__THRD_PRINT(_L("CRequestThread::Receive() dequeing"));
sl@0
   579
			iListLock.Signal();
sl@0
   580
			}
sl@0
   581
		else
sl@0
   582
			{
sl@0
   583
			iIsWaiting=ETrue;
sl@0
   584
			iRequest = NULL;	// set to NULL so we can distinguish between a timer and a request signal
sl@0
   585
			iListLock.Signal();
sl@0
   586
			__THRD_PRINT(_L("CRequestThread::Receive() waiting"));
sl@0
   587
			User::WaitForAnyRequest();
sl@0
   588
			iIsWaiting=EFalse;	// force main thread to post new requests on queue to avoid suspending this thread unnecessarily
sl@0
   589
			}
sl@0
   590
		__THRD_PRINT2(_L("received req 0x%x, func 0x%x"),iRequest, iRequest ? iRequest->Operation()->iFunction : -1);
sl@0
   591
sl@0
   592
		iTimer->RunL();
sl@0
   593
sl@0
   594
		if (iRequest)
sl@0
   595
			iRequest->Process();
sl@0
   596
sl@0
   597
		if(iExit)
sl@0
   598
		    {
sl@0
   599
		    //Any requests that sneaked on to
sl@0
   600
		    //the queue are cancelled in 
sl@0
   601
		    //CRequestThread::ThreadFunction()
sl@0
   602
		    break;
sl@0
   603
		    }
sl@0
   604
		}
sl@0
   605
	}
sl@0
   606
sl@0
   607
void CRequestThread::Deliver(CFsRequest* aRequest,TBool aIsFront, TBool aLowPriority)
sl@0
   608
//
sl@0
   609
// Deliver a request to the list from calling thread
sl@0
   610
// Write request directly to current request if thread is waiting
sl@0
   611
//
sl@0
   612
	{
sl@0
   613
	__THRD_PRINT4(_L("Deliver req %08x to threadId %lx aIsFront=%d iIsWaiting=%d"), aRequest, iThread.Id().Id(), aIsFront, iIsWaiting);
sl@0
   614
	iListLock.Wait();
sl@0
   615
	if (iList.IsEmpty())
sl@0
   616
		{
sl@0
   617
		// if this is a low priority request (and this is the only request in the queue),
sl@0
   618
		// reduce the thread's priority to EPriorityAbsoluteBackground
sl@0
   619
		if (iLowPriority != aLowPriority)
sl@0
   620
			{
sl@0
   621
			__THRD_PRINT(_L("LOWERING THREAD PRIORITY"));
sl@0
   622
			iThread.SetPriority(aLowPriority?EPriorityAbsoluteBackground:EPriorityLess);
sl@0
   623
			iLowPriority = aLowPriority;
sl@0
   624
			}
sl@0
   625
		}
sl@0
   626
	else
sl@0
   627
		{
sl@0
   628
		// there's more than one request in the queue, so rather than go throught the entire queue
sl@0
   629
		// to determine what the thread's priority should be, assume that it should be "high"
sl@0
   630
		if (iLowPriority)
sl@0
   631
			{
sl@0
   632
			iThread.SetPriority(EPriorityLess);
sl@0
   633
			iLowPriority = EFalse;
sl@0
   634
			}
sl@0
   635
		}
sl@0
   636
sl@0
   637
	if(iIsWaiting)
sl@0
   638
		{
sl@0
   639
		// the request thread must be waiting on the iWaitLock
sl@0
   640
		iIsWaiting=EFalse;
sl@0
   641
		iListLock.Signal();
sl@0
   642
		iRequest=aRequest;
sl@0
   643
sl@0
   644
		iThread.RequestSignal();
sl@0
   645
		}
sl@0
   646
	else
sl@0
   647
		{
sl@0
   648
		if(aIsFront)
sl@0
   649
			iList.AddFirst(*aRequest);
sl@0
   650
		else
sl@0
   651
			iList.AddLast(*aRequest);
sl@0
   652
		iListLock.Signal();
sl@0
   653
		}
sl@0
   654
	}
sl@0
   655
sl@0
   656
void CRequestThread::DeliverFront(CFsRequest* aRequest)
sl@0
   657
//
sl@0
   658
//
sl@0
   659
//
sl@0
   660
	{
sl@0
   661
	Deliver(aRequest,ETrue);
sl@0
   662
	}
sl@0
   663
sl@0
   664
void CRequestThread::DeliverBack(CFsRequest* aRequest, TBool aLowPriority)
sl@0
   665
//
sl@0
   666
//
sl@0
   667
//
sl@0
   668
	{
sl@0
   669
	Deliver(aRequest,EFalse,aLowPriority);
sl@0
   670
	}
sl@0
   671
sl@0
   672
sl@0
   673
sl@0
   674
CFsDeltaTimer* CRequestThread::Timer()
sl@0
   675
	{
sl@0
   676
	__ASSERT_ALWAYS(iTimer,Fault(ERequestThreadNotInitialised));
sl@0
   677
	return iTimer;
sl@0
   678
	}
sl@0
   679
sl@0
   680
sl@0
   681
CDriveThread::CDriveThread()
sl@0
   682
	: iFinaliseTimer(FinaliseTimerEvent, this)
sl@0
   683
	{
sl@0
   684
	}
sl@0
   685
sl@0
   686
CDriveThread* CDriveThread::NewL()
sl@0
   687
//
sl@0
   688
//
sl@0
   689
//
sl@0
   690
	{
sl@0
   691
	__PRINT(_L("CDriveThread::NewL()"));
sl@0
   692
	CDriveThread* pT=new(ELeave) CDriveThread;
sl@0
   693
	TInt r=pT->Initialise();
sl@0
   694
	if(r!=KErrNone)
sl@0
   695
		{
sl@0
   696
		delete(pT);
sl@0
   697
		User::Leave(r);
sl@0
   698
		}
sl@0
   699
	return(pT);
sl@0
   700
	}
sl@0
   701
sl@0
   702
TUint CDriveThread::StartL(TInt aDrvNumber)
sl@0
   703
//
sl@0
   704
//
sl@0
   705
//
sl@0
   706
	{
sl@0
   707
	__PRINT1(_L("CDriveThread::StartL() on drive %d"),aDrvNumber);
sl@0
   708
	iDriveNumber=aDrvNumber;
sl@0
   709
	RThread t;
sl@0
   710
	User::LeaveIfError(DoStart(t));
sl@0
   711
	TUint id=t.Id();
sl@0
   712
	return(id);
sl@0
   713
	}
sl@0
   714
sl@0
   715
TInt CDriveThread::DoThreadInitialise()
sl@0
   716
//
sl@0
   717
// Initialise function for the drive thread
sl@0
   718
//  - Renames the thread to contain the drive number.
sl@0
   719
//  - Note: Drive mappings are not available at this time, so we can't show the actual drive letter.
sl@0
   720
//
sl@0
   721
	{
sl@0
   722
	__PRINT1(_L("CDriveThread::DoThreadInitialise() on drive %d"), iDriveNumber);
sl@0
   723
	
sl@0
   724
	TBuf<16> name;
sl@0
   725
	name.Format(_L("DriveThread_%02d"), iDriveNumber);
sl@0
   726
	return(RThread::RenameMe(name));
sl@0
   727
	}
sl@0
   728
sl@0
   729
void CDriveThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
sl@0
   730
//
sl@0
   731
//
sl@0
   732
//
sl@0
   733
	{
sl@0
   734
	__THRD_PRINT1(_L("CDriveThread::CompleteSessionReqeusts() drive=%d"),iDriveNumber);
sl@0
   735
	iListLock.Wait();
sl@0
   736
	TDblQueIter<CFsRequest> q(iList);
sl@0
   737
	CFsRequest* pR;
sl@0
   738
	while((pR=q++)!=NULL)
sl@0
   739
		{
sl@0
   740
		if(pR->Session()==aSession)
sl@0
   741
			{
sl@0
   742
			pR->iLink.Deque();
sl@0
   743
			iListLock.Signal();
sl@0
   744
			pR->Complete(aValue);
sl@0
   745
			iListLock.Wait();
sl@0
   746
			// set iterator back to head of queue in case Complete() has itself removed requests from the queue
sl@0
   747
			q.SetToFirst();
sl@0
   748
			}
sl@0
   749
		}
sl@0
   750
	iListLock.Signal();
sl@0
   751
	__THRD_PRINT(_L("session requests completed"));
sl@0
   752
	}
sl@0
   753
sl@0
   754
sl@0
   755
void CDriveThread::CompleteReadWriteRequests()
sl@0
   756
	{
sl@0
   757
	__THRD_PRINT1(_L("CDriveThread::CompleteReadWriteRequests() drive=%d"),iDriveNumber);
sl@0
   758
sl@0
   759
	iListLock.Wait();
sl@0
   760
sl@0
   761
	TDblQueIter<CFsRequest> q(iList);
sl@0
   762
	CFsRequest* pR;
sl@0
   763
	while((pR=q++)!=NULL)
sl@0
   764
		{
sl@0
   765
		TInt func = pR->Operation()->Function();
sl@0
   766
		if (func == EFsFileRead || func == EFsFileWrite || func == EFsFileWriteDirty)
sl@0
   767
			{
sl@0
   768
			pR->iLink.Deque();
sl@0
   769
			pR->Complete(KErrNotReady);
sl@0
   770
			}
sl@0
   771
		}
sl@0
   772
	iListLock.Signal();
sl@0
   773
sl@0
   774
	__THRD_PRINT(_L("file read/write requests completed"));
sl@0
   775
	}
sl@0
   776
sl@0
   777
/*
sl@0
   778
This function is called by FsThreadManager::SetDriveHung() and attempts to purge the request queue 
sl@0
   779
of all requests which MIGHT belong to the critical notifier server (or to the loader) to avoid causing 
sl@0
   780
a deadlock when calling the server.
sl@0
   781
sl@0
   782
All requests are completed with KErrNotReady apart from :
sl@0
   783
- EFsFileWriteDirty requests, to avoid losing dirty data
sl@0
   784
- KDispatchObjectClose requests as they are raised by the file server only and therefore cannot belong to the critical notifier server
sl@0
   785
- EFsFileSubClose requests, to avoid closing files containing dirty data. These requests have their message completed
sl@0
   786
  so that clients are unblocked, but the request itself is not processed until later. (If the request WAS processed
sl@0
   787
  and completed, then this might result in the CFileCB and CMountCB object being deleted, leading to problems 
sl@0
   788
  dereferencing invalid pointers).
sl@0
   789
*/
sl@0
   790
void CDriveThread::CompleteClientRequests(TInt aValue)
sl@0
   791
	{
sl@0
   792
	__THRD_PRINT1(_L("CDriveThread::CompleteClientRequests() drive=%d"),iDriveNumber);
sl@0
   793
sl@0
   794
	iListLock.Wait();
sl@0
   795
sl@0
   796
	TDblQueIter<CFsRequest> q(iList);
sl@0
   797
	CFsRequest* pR;
sl@0
   798
	while((pR=q++)!=NULL)
sl@0
   799
		{
sl@0
   800
		TInt func = pR->Operation()->Function();
sl@0
   801
		if(func == EFsFileSubClose)
sl@0
   802
			{
sl@0
   803
			TInt msgHandle = pR->Message().Handle();
sl@0
   804
			if ((msgHandle != KLocalMessageHandle) && (msgHandle != 0))
sl@0
   805
				pR->Message().Complete(KErrNone);
sl@0
   806
			}
sl@0
   807
		else if (func != EFsFileWriteDirty && func != KDispatchObjectClose)
sl@0
   808
			{
sl@0
   809
			pR->iLink.Deque();
sl@0
   810
			iListLock.Signal();
sl@0
   811
			pR->Complete(aValue);
sl@0
   812
			iListLock.Wait();
sl@0
   813
			}
sl@0
   814
		}
sl@0
   815
	iListLock.Signal();
sl@0
   816
sl@0
   817
	__THRD_PRINT(_L("client read requests completed"));
sl@0
   818
	}
sl@0
   819
sl@0
   820
TBool CDriveThread::IsRequestWriteable()
sl@0
   821
//
sl@0
   822
// return if current request may cause write to disk
sl@0
   823
// must be called from drive thread
sl@0
   824
//
sl@0
   825
	{
sl@0
   826
	__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadWriteable));
sl@0
   827
	return(iRequest->Operation()->IsWrite());
sl@0
   828
	}
sl@0
   829
sl@0
   830
TBool CDriveThread::IsSessionNotifyUser()
sl@0
   831
//
sl@0
   832
// return if request's session has notify user set
sl@0
   833
// must be called from drive thread and request have a session set
sl@0
   834
//
sl@0
   835
	{
sl@0
   836
	__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadNotifyUser1));
sl@0
   837
	// NB For read-ahead or a flush-dirty write request generated by the file cache, the session will be NULL: 
sl@0
   838
	// in this case assume that notify user is set (as it's the safest option)
sl@0
   839
	CSessionFs* session = iRequest->Session();
sl@0
   840
	return session ?  session->GetNotifyUser() : ETrue;
sl@0
   841
	}
sl@0
   842
sl@0
   843
void CDriveThread::StartFinalisationTimer()
sl@0
   844
	{
sl@0
   845
	if(IsProxyDrive(iDriveNumber))
sl@0
   846
		iFinaliseTimer.Start(this, KFinaliseTimerPeriod);
sl@0
   847
	}
sl@0
   848
	
sl@0
   849
sl@0
   850
void CDriveThread::StopFinalisationTimer()
sl@0
   851
	{
sl@0
   852
	iFinaliseTimer.Stop();
sl@0
   853
	}
sl@0
   854
sl@0
   855
TInt CDriveThread::FinaliseTimerEvent(TAny* aSelfP)
sl@0
   856
	{
sl@0
   857
	CDriveThread& self = *(CDriveThread*)aSelfP;
sl@0
   858
sl@0
   859
	TDrive& drive = TheDrives[self.iDriveNumber];
sl@0
   860
	if(drive.IsMounted())
sl@0
   861
        {
sl@0
   862
        if (drive.CurrentMount().LockStatus() == 0)
sl@0
   863
            {
sl@0
   864
            // Ignore the error here, as there's nothing we can do about it...
sl@0
   865
            (void)drive.FinaliseMount(RFs::EFinal_RW);
sl@0
   866
            }
sl@0
   867
        else
sl@0
   868
            {
sl@0
   869
            self.StartFinalisationTimer();
sl@0
   870
            }
sl@0
   871
        }
sl@0
   872
sl@0
   873
	return KErrNone;
sl@0
   874
	}
sl@0
   875
sl@0
   876
sl@0
   877
CDisconnectThread::~CDisconnectThread()
sl@0
   878
//
sl@0
   879
//
sl@0
   880
//
sl@0
   881
	{
sl@0
   882
	if(iRequest)
sl@0
   883
		delete(iRequest);
sl@0
   884
	}
sl@0
   885
sl@0
   886
sl@0
   887
CDisconnectThread* CDisconnectThread::NewL()
sl@0
   888
//
sl@0
   889
//
sl@0
   890
//
sl@0
   891
	{
sl@0
   892
	__THRD_PRINT(_L("CDisconnectThread::NewL()"));
sl@0
   893
	CDisconnectThread* pT=new(ELeave) CDisconnectThread;
sl@0
   894
	TInt r=pT->Initialise();
sl@0
   895
	if(r!=KErrNone)
sl@0
   896
		{
sl@0
   897
		delete(pT);
sl@0
   898
		User::Leave(r);
sl@0
   899
		}
sl@0
   900
	return(pT);
sl@0
   901
	}
sl@0
   902
sl@0
   903
TUint CDisconnectThread::StartL()
sl@0
   904
//
sl@0
   905
//
sl@0
   906
//
sl@0
   907
	{
sl@0
   908
	__PRINT(_L("CDisconnectThread::StartL()"));
sl@0
   909
	iRequest = new(ELeave) CFsInternalRequest;
sl@0
   910
	__THRD_PRINT1(_L("internal request = 0x%x"),iRequest);
sl@0
   911
	iRequest->Set(CancelSessionOp,NULL);	
sl@0
   912
	
sl@0
   913
	RThread t;
sl@0
   914
	TInt r=DoStart(t);
sl@0
   915
	if(r!=KErrNone)
sl@0
   916
		{
sl@0
   917
		delete(iRequest);
sl@0
   918
		iRequest=NULL;
sl@0
   919
		User::Leave(r);
sl@0
   920
		}
sl@0
   921
	iRequest->SetThreadHandle(t.Handle());
sl@0
   922
	__THRD_PRINT1(_L("CDisconnect::StartL() handle=%d"),t.Handle());
sl@0
   923
	iRequest->SetAllocated();
sl@0
   924
	TUint id=t.Id();
sl@0
   925
	return(id);
sl@0
   926
	}
sl@0
   927
sl@0
   928
sl@0
   929
CPluginThread::CPluginThread(CFsPlugin& aPlugin)
sl@0
   930
  : iPlugin(aPlugin)
sl@0
   931
	{
sl@0
   932
	/** @prototype */
sl@0
   933
	iOperationLock.Close();
sl@0
   934
	iPlugin.Open();
sl@0
   935
	}
sl@0
   936
sl@0
   937
CPluginThread::~CPluginThread()
sl@0
   938
    {
sl@0
   939
    iPlugin.Close();
sl@0
   940
    }
sl@0
   941
sl@0
   942
sl@0
   943
CPluginThread* CPluginThread::NewL(CFsPlugin& aPlugin)
sl@0
   944
	{
sl@0
   945
	__PRINT(_L("CPluginThread::NewL()"));
sl@0
   946
	CPluginThread* pT=new(ELeave) CPluginThread(aPlugin);
sl@0
   947
	TInt r=pT->Initialise();
sl@0
   948
sl@0
   949
	/** @prototype */
sl@0
   950
	if(r == KErrNone)
sl@0
   951
		r=pT->iOperationLock.CreateLocal(0);
sl@0
   952
sl@0
   953
	if(r!=KErrNone)
sl@0
   954
		{
sl@0
   955
		delete(pT);
sl@0
   956
		User::Leave(r);
sl@0
   957
		}
sl@0
   958
	return(pT);
sl@0
   959
	}
sl@0
   960
sl@0
   961
TUint CPluginThread::StartL()
sl@0
   962
	{
sl@0
   963
	__PRINT(_L("CPluginThread::StartL()"));
sl@0
   964
	RThread t;
sl@0
   965
	User::LeaveIfError(DoStart(t));
sl@0
   966
	TUint id=t.Id();
sl@0
   967
	return(id);
sl@0
   968
	}
sl@0
   969
sl@0
   970
void CPluginThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
sl@0
   971
	{
sl@0
   972
	__THRD_PRINT(_L("CPluginThread::CompleteSessionRequests()"));
sl@0
   973
	iListLock.Wait();
sl@0
   974
	TDblQueIter<CFsRequest> q(iList);
sl@0
   975
	CFsRequest* pR;
sl@0
   976
	while((pR=q++)!=NULL)
sl@0
   977
		{
sl@0
   978
		if(pR->Session()==aSession)
sl@0
   979
			{
sl@0
   980
			pR->iLink.Deque();
sl@0
   981
			pR->Complete(aValue);
sl@0
   982
			}
sl@0
   983
		}
sl@0
   984
	iListLock.Signal();
sl@0
   985
	__THRD_PRINT(_L("session requests completed"));
sl@0
   986
	}
sl@0
   987
sl@0
   988
TInt CPluginThread::DoThreadInitialise()
sl@0
   989
	{
sl@0
   990
	__PRINT(_L("CPluginThread::DoThreadInitialise()"));
sl@0
   991
 	TRAPD(err, iPlugin.InitialiseL());
sl@0
   992
sl@0
   993
	return err;
sl@0
   994
	}
sl@0
   995
sl@0
   996
/** @prototype */
sl@0
   997
void CPluginThread::OperationLockWait()
sl@0
   998
	{
sl@0
   999
	iOperationLock.Wait();
sl@0
  1000
	}
sl@0
  1001
sl@0
  1002
/** @prototype */
sl@0
  1003
void CPluginThread::OperationLockSignal()
sl@0
  1004
	{
sl@0
  1005
	iOperationLock.Signal();
sl@0
  1006
	}
sl@0
  1007
sl@0
  1008
// Class TTickCountQue
sl@0
  1009
/**
sl@0
  1010
@internalComponent
sl@0
  1011
@released
sl@0
  1012
sl@0
  1013
Constructs an empty list header
sl@0
  1014
*/
sl@0
  1015
TTickCountQue::TTickCountQue()
sl@0
  1016
	{}
sl@0
  1017
sl@0
  1018
sl@0
  1019
sl@0
  1020
sl@0
  1021
/**
sl@0
  1022
@internalComponent
sl@0
  1023
@released
sl@0
  1024
sl@0
  1025
Adds the specified list element.
sl@0
  1026
sl@0
  1027
The element is added into the list in order of its tick count.
sl@0
  1028
sl@0
  1029
@param aRef The list element to be inserted.
sl@0
  1030
*/
sl@0
  1031
void TTickCountQue::Add(TTickCountQueLink& aRef)
sl@0
  1032
	{
sl@0
  1033
	TTickCountQueLink* currentLink = (TTickCountQueLink*)(iHead.iNext);
sl@0
  1034
	TTickCountQueLink* addLink = &aRef;
sl@0
  1035
sl@0
  1036
	while (	(currentLink != (TTickCountQueLink*)&iHead) &&
sl@0
  1037
			(((TInt)(addLink->iTickCount - currentLink->iTickCount)) >= 0)
sl@0
  1038
		)
sl@0
  1039
		{
sl@0
  1040
		currentLink = (TTickCountQueLink*)currentLink->iNext;
sl@0
  1041
		}
sl@0
  1042
sl@0
  1043
	addLink->Enque(currentLink->iPrev);
sl@0
  1044
	}
sl@0
  1045
sl@0
  1046
sl@0
  1047
sl@0
  1048
sl@0
  1049
/**
sl@0
  1050
@internalComponent
sl@0
  1051
@released
sl@0
  1052
sl@0
  1053
Removes the first list element from the linked list if its tick count
sl@0
  1054
is prior to the current tick count.
sl@0
  1055
sl@0
  1056
@param aTickCount The current tick count.
sl@0
  1057
sl@0
  1058
@return A pointer to the element removed from the linked list. This is NULL 
sl@0
  1059
        if the first element has yet to expire or the queue is empty.
sl@0
  1060
*/
sl@0
  1061
TTickCountQueLink* TTickCountQue::RemoveFirst(TUint aTickCount)
sl@0
  1062
	{
sl@0
  1063
	TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
sl@0
  1064
sl@0
  1065
	if (((TInt)(firstLink->iTickCount - aTickCount)) <= 0)
sl@0
  1066
		{
sl@0
  1067
		return RemoveFirst();
sl@0
  1068
		}
sl@0
  1069
	else
sl@0
  1070
		{
sl@0
  1071
		return NULL;
sl@0
  1072
		}
sl@0
  1073
	}
sl@0
  1074
sl@0
  1075
sl@0
  1076
/**
sl@0
  1077
@internalComponent
sl@0
  1078
@released
sl@0
  1079
sl@0
  1080
Removes the first list element from the linked list, if any.
sl@0
  1081
sl@0
  1082
@return A pointer to the element removed from the linked list. This is NULL, 
sl@0
  1083
        if the queue is empty.
sl@0
  1084
*/
sl@0
  1085
TTickCountQueLink* TTickCountQue::RemoveFirst()
sl@0
  1086
	{
sl@0
  1087
	TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
sl@0
  1088
sl@0
  1089
	if (firstLink != (TTickCountQueLink*)&iHead)
sl@0
  1090
		{
sl@0
  1091
		firstLink->Deque();
sl@0
  1092
		return firstLink;
sl@0
  1093
		}
sl@0
  1094
sl@0
  1095
	return NULL;
sl@0
  1096
	}
sl@0
  1097
sl@0
  1098
sl@0
  1099
sl@0
  1100
sl@0
  1101
/**
sl@0
  1102
@internalComponent
sl@0
  1103
@released
sl@0
  1104
sl@0
  1105
Gets a pointer to the first list element in the doubly linked list.
sl@0
  1106
sl@0
  1107
@return A pointer to the first list element in the doubly linked list. If 
sl@0
  1108
        the list is empty, this pointer is not necessarily NULL and must not
sl@0
  1109
		be assumed to point to a valid object.
sl@0
  1110
*/
sl@0
  1111
TTickCountQueLink* TTickCountQue::First() const
sl@0
  1112
	{
sl@0
  1113
#if defined (_DEBUG)
sl@0
  1114
	__DbgTestEmpty();
sl@0
  1115
#endif
sl@0
  1116
    return((TTickCountQueLink*)iHead.iNext);
sl@0
  1117
    }
sl@0
  1118
sl@0
  1119
sl@0
  1120
sl@0
  1121
sl@0
  1122
sl@0
  1123
CFsDeltaTimer* CFsDeltaTimer::New(CRequestThread& aRequestThread, TInt aPriority)
sl@0
  1124
	{
sl@0
  1125
	TTimeIntervalMicroSeconds32 tickPeriod;
sl@0
  1126
	UserHal::TickPeriod(tickPeriod);
sl@0
  1127
sl@0
  1128
	CFsDeltaTimer* timer = new CFsDeltaTimer(aRequestThread, aPriority, tickPeriod.Int());
sl@0
  1129
	if (timer == NULL)
sl@0
  1130
		return NULL;
sl@0
  1131
sl@0
  1132
	if (timer->iTimer.CreateLocal() != KErrNone || 
sl@0
  1133
		timer->iLock.CreateLocal() != KErrNone)
sl@0
  1134
		{
sl@0
  1135
		delete timer;
sl@0
  1136
		return NULL;
sl@0
  1137
		}
sl@0
  1138
sl@0
  1139
	return timer;
sl@0
  1140
	}
sl@0
  1141
sl@0
  1142
CFsDeltaTimer::CFsDeltaTimer(CRequestThread& aRequestThread, TInt /*aPriority*/, TInt aTickPeriod) : 
sl@0
  1143
	iRequestThread(aRequestThread), iTickPeriod(aTickPeriod)
sl@0
  1144
	{
sl@0
  1145
	iThreadId = RThread().Id(); 
sl@0
  1146
	}
sl@0
  1147
sl@0
  1148
/**
sl@0
  1149
Destructor.
sl@0
  1150
sl@0
  1151
Frees all resources before destruction of the object. Specifically, it cancels 
sl@0
  1152
any outstanding timer requests generated by the RTimer object and then deletes 
sl@0
  1153
all timed event entries from the timed event queue.
sl@0
  1154
sl@0
  1155
@see RTimer
sl@0
  1156
sl@0
  1157
@publishedAll
sl@0
  1158
@released
sl@0
  1159
*/
sl@0
  1160
CFsDeltaTimer::~CFsDeltaTimer()
sl@0
  1161
	{
sl@0
  1162
	Cancel();
sl@0
  1163
sl@0
  1164
	while (!iQueue.IsEmpty())
sl@0
  1165
		{
sl@0
  1166
		iQueue.First()->Deque();
sl@0
  1167
		}
sl@0
  1168
sl@0
  1169
	iLock.Close();
sl@0
  1170
	iTimer.Close();
sl@0
  1171
	}
sl@0
  1172
sl@0
  1173
sl@0
  1174
/**
sl@0
  1175
Start the timer.
sl@0
  1176
sl@0
  1177
@see RTimer
sl@0
  1178
sl@0
  1179
@publishedAll
sl@0
  1180
@released
sl@0
  1181
*/
sl@0
  1182
void CFsDeltaTimer::Start(TThreadTimer& aEntry, TTimeIntervalMicroSeconds32 aTime)
sl@0
  1183
	{
sl@0
  1184
	iLock.Wait();
sl@0
  1185
sl@0
  1186
	// must be already running on this thread or not running at all
sl@0
  1187
	ASSERT(aEntry.iRequestThread == NULL || aEntry.iRequestThread  == &iRequestThread);
sl@0
  1188
sl@0
  1189
	// attach the entry to this thread
sl@0
  1190
	aEntry.iRequestThread = &iRequestThread;
sl@0
  1191
sl@0
  1192
	// Remove the entry from the list (if it's already queued) 
sl@0
  1193
	// and then add it again in the correct order
sl@0
  1194
	aEntry.iLink.Deque();
sl@0
  1195
	QueueLong(TTimeIntervalMicroSeconds(MAKE_TINT64(0, aTime.Int())), aEntry);
sl@0
  1196
sl@0
  1197
	iLock.Signal();
sl@0
  1198
	}
sl@0
  1199
sl@0
  1200
void CFsDeltaTimer::Stop(TThreadTimer& aEntry)
sl@0
  1201
	{
sl@0
  1202
	iLock.Wait();
sl@0
  1203
sl@0
  1204
	aEntry.iLink.Deque();
sl@0
  1205
	aEntry.iRequestThread = NULL;
sl@0
  1206
sl@0
  1207
	iLock.Signal();
sl@0
  1208
	}
sl@0
  1209
sl@0
  1210
sl@0
  1211
TInt CFsDeltaTimer::QueueLong(TTimeIntervalMicroSeconds aTimeInMicroSeconds, TThreadTimer& aEntry)
sl@0
  1212
	{
sl@0
  1213
	const TInt64 timeInTicks = (aTimeInMicroSeconds.Int64() + iTickPeriod - 1) / iTickPeriod;
sl@0
  1214
sl@0
  1215
	TInt timeInTicks32 = I64LOW(timeInTicks);
sl@0
  1216
sl@0
  1217
	// We are using deltas on tick values, hence using maximum signed number of ticks
sl@0
  1218
	if (I64HIGH(timeInTicks) || (timeInTicks32 < 0))
sl@0
  1219
		{
sl@0
  1220
		return KErrOverflow;
sl@0
  1221
		}
sl@0
  1222
sl@0
  1223
	// Make sure we queue for at least one tick
sl@0
  1224
	if (timeInTicks32 == 0)
sl@0
  1225
		{
sl@0
  1226
		timeInTicks32 = 1;
sl@0
  1227
		}
sl@0
  1228
	
sl@0
  1229
	// Calculate tick count for new entry
sl@0
  1230
	aEntry.iLink.iTickCount = User::TickCount() + timeInTicks32;
sl@0
  1231
sl@0
  1232
	// Add this entry at the right spot
sl@0
  1233
	iQueue.Add(aEntry.iLink);
sl@0
  1234
sl@0
  1235
	// we only need to re-start the timer if we've added an entry to the head of the queue
sl@0
  1236
	// or the timer is not already running
sl@0
  1237
	if (&aEntry.iLink == iQueue.First() || iStatus == KRequestPending)
sl@0
  1238
		Activate();
sl@0
  1239
	
sl@0
  1240
	return KErrNone;
sl@0
  1241
	}
sl@0
  1242
sl@0
  1243
void CFsDeltaTimer::Activate()
sl@0
  1244
//
sl@0
  1245
// Queue a request on the timer.
sl@0
  1246
//
sl@0
  1247
	{
sl@0
  1248
	if (RThread().Id() != iThreadId)
sl@0
  1249
		{
sl@0
  1250
		iRestartNeeded = ETrue;
sl@0
  1251
		iRequestThread.iThread.RequestSignal();
sl@0
  1252
		return;
sl@0
  1253
		}
sl@0
  1254
sl@0
  1255
	if (iStatus == KRequestPending)
sl@0
  1256
		Cancel();
sl@0
  1257
sl@0
  1258
	if (!iQueue.IsEmpty() && !iQueueBusy)
sl@0
  1259
		{
sl@0
  1260
		const TInt ticksToWait = iQueue.First()->iTickCount - User::TickCount();
sl@0
  1261
sl@0
  1262
		if (ticksToWait > 0)
sl@0
  1263
			{
sl@0
  1264
			iTimer.AfterTicks(iStatus, ticksToWait);
sl@0
  1265
			}
sl@0
  1266
		else
sl@0
  1267
			{
sl@0
  1268
			TRequestStatus* status = &iStatus;
sl@0
  1269
			User::RequestComplete(status, KErrNone);
sl@0
  1270
			}
sl@0
  1271
		}
sl@0
  1272
	}
sl@0
  1273
sl@0
  1274
sl@0
  1275
sl@0
  1276
void CFsDeltaTimer::RunL()
sl@0
  1277
//
sl@0
  1278
// Call all zero delta callbacks
sl@0
  1279
	{
sl@0
  1280
	// if still running and no restart needed, then there's nothing to do
sl@0
  1281
	if (iStatus == KRequestPending && !iRestartNeeded)
sl@0
  1282
		return;
sl@0
  1283
sl@0
  1284
sl@0
  1285
	iLock.Wait();
sl@0
  1286
sl@0
  1287
	// Queue busy
sl@0
  1288
	iQueueBusy = ETrue;
sl@0
  1289
sl@0
  1290
	// Whilst the list of expired timers is being processed, time will pass and
sl@0
  1291
	// the tick count may have increased such that there are now more expired
sl@0
  1292
	// timers.  Loop until we have either emptied the queue or can wait for a
sl@0
  1293
	// timer exipration in the future.
sl@0
  1294
	if (iStatus == KErrNone)
sl@0
  1295
		{
sl@0
  1296
		iStatus = KErrNotReady;
sl@0
  1297
		while (!iQueue.IsEmpty())
sl@0
  1298
			{
sl@0
  1299
			// Calculate how long till first timer expires
sl@0
  1300
			const TUint tickCount = User::TickCount();
sl@0
  1301
sl@0
  1302
			// If the first timer is yet to expire, wait some more
sl@0
  1303
			if (((TInt)(iQueue.First()->iTickCount - tickCount)) > 0)
sl@0
  1304
				{
sl@0
  1305
				break;
sl@0
  1306
				}
sl@0
  1307
sl@0
  1308
			// Remove entry before callback to prevent re-entrancy issues
sl@0
  1309
			TTickCountQueLink* entry = iQueue.RemoveFirst();
sl@0
  1310
sl@0
  1311
			// Iterate through the timers we know have expired based on the
sl@0
  1312
			// last calculation of delta
sl@0
  1313
			while (entry)
sl@0
  1314
				{
sl@0
  1315
				TThreadTimer* threadTimer = reinterpret_cast<TThreadTimer*>(PtrSub(entry, _FOFF(TThreadTimer, iLink)));
sl@0
  1316
				threadTimer->iRequestThread = NULL;	// indicate timer not running
sl@0
  1317
sl@0
  1318
				// Make callback.  This could go reentrant on Queue[Long]() or Remove().
sl@0
  1319
				iLock.Signal();
sl@0
  1320
				threadTimer->iCallBack.CallBack();
sl@0
  1321
				iLock.Wait();
sl@0
  1322
sl@0
  1323
				// Remove the next expired entry, if any
sl@0
  1324
				entry = iQueue.RemoveFirst(tickCount);
sl@0
  1325
				}
sl@0
  1326
			}
sl@0
  1327
		}
sl@0
  1328
sl@0
  1329
	// Queue idle
sl@0
  1330
	iQueueBusy = EFalse;
sl@0
  1331
sl@0
  1332
sl@0
  1333
	// Requeue timer if queue isn't empty
sl@0
  1334
	Activate();
sl@0
  1335
sl@0
  1336
	iRestartNeeded = EFalse;
sl@0
  1337
sl@0
  1338
	iLock.Signal();
sl@0
  1339
	}
sl@0
  1340
	
sl@0
  1341
void CFsDeltaTimer::Cancel()
sl@0
  1342
	{
sl@0
  1343
	if (iStatus == KRequestPending)
sl@0
  1344
		{
sl@0
  1345
		iTimer.Cancel();
sl@0
  1346
		User::WaitForRequest(iStatus);
sl@0
  1347
		}
sl@0
  1348
	}
sl@0
  1349
sl@0
  1350
sl@0
  1351
sl@0
  1352
TThreadTimer::TThreadTimer(TInt (*aCallBackFunction)(TAny*),TAny* aPtr) :
sl@0
  1353
	iCallBack(aCallBackFunction, aPtr),
sl@0
  1354
	iRequestThread(NULL)
sl@0
  1355
	{
sl@0
  1356
	};	
sl@0
  1357
sl@0
  1358
sl@0
  1359
void TThreadTimer::Start(CRequestThread* aRequestThread, TTimeIntervalMicroSeconds32 aTime)
sl@0
  1360
	{
sl@0
  1361
	ASSERT(aRequestThread);
sl@0
  1362
sl@0
  1363
	// NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL()
sl@0
  1364
	// may be running in another thread and set iRequestThread to NULL
sl@0
  1365
	CRequestThread* requestThread = iRequestThread;
sl@0
  1366
	if (!requestThread)	// if not already running, use caller's request thread
sl@0
  1367
		requestThread = aRequestThread;
sl@0
  1368
sl@0
  1369
sl@0
  1370
	__ASSERT_DEBUG(requestThread->Timer(),Fault(ERequestThreadNotInitialised));
sl@0
  1371
	requestThread->Timer()->Start(*this, aTime);
sl@0
  1372
	}
sl@0
  1373
sl@0
  1374
sl@0
  1375
void TThreadTimer::Stop()
sl@0
  1376
	{
sl@0
  1377
	// NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL()
sl@0
  1378
	// may be running in another thread and set iRequestThread to NULL
sl@0
  1379
	CRequestThread* requestThread = iRequestThread;
sl@0
  1380
	if (requestThread)
sl@0
  1381
		requestThread->Timer()->Stop(*this);
sl@0
  1382
	}
sl@0
  1383
sl@0
  1384