userlibandfileserver/fileserver/sfile/sf_notifier.h
changeset 0 a41df078684a
child 299 b5a01337d018
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_notifier.h	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,430 @@
+// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// f32\sfile\sf_notifier.h
+// 
+//
+
+#include "sf_std.h"
+#include "sf_pool.h"
+#include "e32hashtab.h" 
+#include "cl_notification.h"
+#include "f32notification.h"
+
+#ifndef SF_NOTIFIER_H
+#define SF_NOTIFIER_H
+
+/**
+ * Number of notifications in TFsNotificationType that the client can set
+ * @internalTechnology
+ */
+const TInt KNumRegisterableFilters = 8; 
+
+/*
+ * This determines the size of the CFsPool.
+ * Until we have read/write locks there is no point in this being more than 1.
+ * 
+ * @internalTechnology
+ */
+const TInt KNotificationPoolSize = 1;
+
+
+/**
+ * A CFsNotificationPathFilter is a simple class containing two HBufCs of the target to be notified about:
+ * 1 for the drive and path
+ * 1 for the filename
+ *  
+ *  A CFsNotificationPathFilter has a 1 to Many relationship with TFsNotificationTypeFilter
+ *  
+ * @internalTechnology
+ */
+class CFsNotificationPathFilter
+	{
+public:
+	static CFsNotificationPathFilter* NewL(const TDesC& aPath, const TDesC& aFilename);
+	~CFsNotificationPathFilter();
+private:
+	void ConstructL(const TDesC& aPath, const TDesC& aFilename);
+	CFsNotificationPathFilter();
+public:
+	HBufC* iPath;
+	HBufC* iFilename;
+	};
+
+/**
+ * A TFsNotificationTypeFilter is a class which contains a pointer to the associated path to monitor
+ * and the type of notification.
+ * 
+ * @internalTechnology
+ */
+class TFsNotificationTypeFilter
+	{
+public:
+	CFsNotificationPathFilter* iPathFilter;
+	TFsNotification::TFsNotificationType iNotificationType; 
+	//As we are storing filters in filter-specific
+	//arrays then iNotificationType is potentially redundant.
+	};
+
+//These typedefs are to appease the compiler as it does not like
+//nested templated arguments.
+ /**
+  *  @internalTechnology
+  */
+typedef RArray<TFsNotificationTypeFilter> TFsNotificationTypeArray;
+/**
+ * @internalTechnology
+ */
+typedef RArray<TFsNotificationTypeArray> TFsNotificationTypeDriveArray;
+
+class CFsNotificationBlock; //forward decl.
+
+/**
+ * CFsNotifyRequest is a file-server side object representation of an RFsNotify sub-session.   
+ * 
+ * @internalTechnology
+ */
+NONSHARABLE_CLASS(CFsNotifyRequest) : public CFsObject
+	{
+public:
+	
+	/*
+	 * Active means that the client is waiting for the first notification to be sent
+	 * The status then changes to Outstanding when further notifications are sent to the buffer
+	 * If the server overflows when in state EOutstanding then we move to state EOutstandingOverflow.
+	 * From EOutstandingOverflow after the next call to RequestNotifications from the client, we must move to state EInactive.
+	 * If we do not overflow then when RequestNotifications is called we can move back to state EActive.
+	 * EInactive is when there are no notifications outstanding and request notifications hasn't been called. 
+	 */
+	enum TNotifyRequestStatus
+		{
+		EActive,				//Server waiting for a notification (filters added, RequestNotifications called)
+		EOutstanding,			//Server waiting for client to call RequestNotifications, has notification(s) to send.
+		EOutstandingOverflow,	//Server waiting for client to call RequestNotifications, has notification(s) to send, buffer has overflowed.
+		EInactive				//Server waiting for RequestNotification, no notifications outstanding 
+		};
+	
+	static CFsNotifyRequest* NewL();
+	virtual ~CFsNotifyRequest();
+	
+	/*
+	 * Returns the RArray<TFsNotificationFilter> for an index
+	 * as returned from FsNotificationHelper::TypeToIndex()
+	 */
+	TFsNotificationTypeArray* FilterTypeList(TInt aDrive,TInt aIndex);
+	
+	//Removes all filters from iFilterList
+	TInt RemoveFilters();
+
+	//Add single filter to this request
+	TInt AddFilterL(CFsNotificationPathFilter* aFilter, TUint aMask);
+
+	//Sets filter as active/inactive
+	void SetActive(TNotifyRequestStatus aValue);
+	
+	/**
+	 *Get the status of this request.
+	 *@See TNotifyRequestStatus 
+	 */ 
+	TNotifyRequestStatus ActiveStatus();
+	
+	/*
+	 * Completes and frees notification request
+	 * 
+	 * @param aIsCancel is used to determine whether 
+	 * to write back to the client or not when aReason != KErrNone.
+	 * 
+	 * In the case of closing the subsession you shouldn't write back to the client.
+	 */
+	void CompleteClientRequest(TInt aReason,TBool aIsCancel=EFalse);
+
+	//RfsNotify::RequestNotifications has been called
+	TInt SetClientMessage(const RMessage2& aClientMsg);
+
+	/* 
+	 * Called from FsNotificationManager::HandleChange(),
+	 * this function packages the data in to a CFsNotificationBlock in preperation for 
+	 * notification of this operation to the client.
+	 * 
+	 * Calling of this function means that we are notifying about this operation. (all checks passed)
+	 * 
+	 * aRequest can be NULL when the request doesn't come from the file server 
+	 * (such as when a media card is removed)
+	 */
+	TInt NotifyChange(CFsClientMessageRequest* aRequest, const TDesC& aName, TFsNotification::TFsNotificationType aNotificationType, CFsNotificationBlock& aBlock);
+		
+	/*
+	 * This function performs the IPC to the client's buffer.
+	 */
+	TInt SynchroniseBuffer(CFsNotificationBlock& aBlock,TInt aServerTail, TInt aNotificationSize);
+	
+	//Closing this notification
+	void CloseNotification();
+
+	//Simple getter
+	TInt ClientMsgHandle();
+	
+private:
+	CFsNotifyRequest();
+	void ConstructL();
+	
+	//Check whether there is room for a new notification in the client's buffer
+	TBool ValidateNotification(TInt aNotificationSize, TInt& aServerTail);
+	
+	/*
+	 * The iTailSemaphore is used so that many CFsNotificationBlocks can
+	 * be processed concurrently.
+	 * This lock ensures that the iServerTail is safe.
+	 */
+	 //ToDo:  This should be a ReadWriteLock
+	RFastLock iTailSemaphore;
+	
+	/*
+	 * The iClientSyncLock is a Read/Write style lock whereby it is 
+	 * set up with the value of KNotificationPoolSize.
+	 * When a block is allocated it calls wait on this lock.
+	 * 
+	 * This lock is to ensure that all of the currently processing blocks are
+	 * written before the client is updated.
+	 * 
+	 * i.e. if two blocks are being processed concurrently and the second 
+	 * block is written we need to wait until the first one is also written
+	 * before the client receives the updated tail.
+	 */
+	//ToDo: This should be a ReadWriteLock
+	RFastLock iClientSyncLock;
+	
+
+	/*
+	 * HashMap<DriveNumber, TFsNotificationTypeDriveArray>
+	 * HashMap<DriveNumber, RArray<TFsNotificationTypeArray>>
+	 * HashMap<DriveNumber, RArray<RArray<TFsNotificationTypeFilter>>>
+	 * 
+	 * Each value of iDrivesTypesFiltersMap is of type TFsNotificationTypeDriveArray 
+	 * associated with a particular drive.
+	 * 
+	 * Each index of the TFsNotificationTypeDriveArray is a TFsNotificationTypeArray
+	 */
+	RHashMap<TInt,TFsNotificationTypeDriveArray> iDrivesTypesFiltersMap;
+	
+	/*
+	 * The iPathFilterList is an RPointerArray of CFsNotificationPathFilters.
+	 * 
+	 * These are normally only accessed via a TFsNotificationTypeFilter (via iDrivesTypesFiltersMap),
+	 * not via this array directly.
+	 */
+	RPointerArray<CFsNotificationPathFilter> iPathFilterList;
+	
+	RMessage2 iBufferMsg; //To update buffer
+	RMessage2 iClientMsg; //client notification request
+	
+	CSessionFs* iSession; //Session associated with this request (TFsSessionDisconnect::DoRequestL)
+	
+	TNotifyRequestStatus iNotifyRequestStatus;	//Current status of this request
+	
+	//The following 3 variables must be aligned when modified.
+	TInt iClientHead;	//Offset where the client should start reading from.
+						//If the server writes past this offset we must overflow the client.
+	
+	TInt iClientTail;	//The end of the client's accessible range.
+	
+	TInt iServerTail;	//The end of the server's accessible range.
+						//Overflow occurs if iServerTail becomes more than iClientHead.
+	
+	TInt iClientBufferSize;		//Buffer size is word-aligned.
+	
+	friend class TFsNotificationBuffer;	//For access to iClientBufferSize and iBufferMsg
+	friend class TFsNotificationRequest;//For access to iClientBufferSize
+	friend class FsNotificationManager; //For access to iSession
+	friend class TFsNotificationOpen; //For access to iSession
+	friend class TFsNotificationRemove; //For access to iDrivesTypesFiltersMap
+	};
+
+/**
+ * A CFsNotificationBlock is a chunk of memory which is used to represent a notification
+ * such that a single IPC can be performed from server to client.
+ * 
+ * CFsNotificationBlocks are stored in a CFsPool<CFsNotificationBlock>. 
+ * 
+ *@internalTechnology
+ */
+class CFsNotificationBlock
+	{
+public:
+	static CFsNotificationBlock* New();
+	~CFsNotificationBlock();
+	TAny* Data();
+private:
+	CFsNotificationBlock();
+	TText8 iData[KMinNotificationBufferSize];
+	};
+
+/**
+ * Helper class to get certain attributes from or about a particular operation to used in a notification
+ * 
+ * @internalTechnology
+ */
+class FsNotificationHelper
+	{
+public:
+	static void NotificationType(TInt aFunction,TFsNotification::TFsNotificationType& aNotificationType);
+	static void PathName(CFsClientMessageRequest& aRequest, TDes& aName);
+	static void NewPathName(CFsClientMessageRequest& aRequest, TPtrC& aName);
+	static TInt NotificationSize(CFsClientMessageRequest& aRequest, TFsNotification::TFsNotificationType aNotificationType, const TDesC& aName);
+	static TInt TypeToIndex(TFsNotification::TFsNotificationType aType);
+	static TFsNotification::TFsNotificationType NotificationType(TInt& aIndex);
+	static TInt DriveNumber(const TPtrC& aPath);
+	static void Attributes(CFsClientMessageRequest& aRequest, TUint& aSet, TUint& aClear);
+	};
+
+/**
+ * The FsNotificationManager is a static object
+ * 
+ *@internalTechnology
+ */
+class FsNotificationManager
+	{
+public:
+	//New notification request from client
+	static void AddNotificationRequestL(CFsNotifyRequest* aNotificationRequest);
+
+	//Notification request cancel
+	static void RemoveNotificationRequest(CFsNotifyRequest* aNotificationRequest);
+	//Notification request cancel (session closed)
+	static void RemoveNotificationRequest(CSessionFs* aSession);
+
+	/* A change has occurred represented by this request.
+	 * Work out which CFsNotifyRequests are interested
+	 * (if any) and call CFsNotifyRequest::NotifyChange.
+	 */
+	static void HandleChange(CFsClientMessageRequest& aRequest);
+	
+	/* A change has occurred represented by this request.
+	 * Work out which CFsNotifyRequests are interested
+	 * (if any) and call CFsNotifyRequest::NotifyChange.
+	 * 
+	 * This override is used directly when we want to force a particular notification type
+	 */
+	static void HandleChange(CFsClientMessageRequest& aRequest, TFsNotification::TFsNotificationType aType);
+	
+	/* 
+	 * This override is used directly when we want to specify the current operation's name (src) and notification type.
+	 * 
+	 * aRequest can be NULL when the request doesn't come from the file server 
+	 * such as when a media card is removed, see LocalDrives::CompleteDriveNotifications
+	 * 
+	 * @See LocalDrives::CompleteDriveNotifications(TInt aDrive)
+	 */
+	static void HandleChange(CFsClientMessageRequest* aRequest, const TDesC& aOperationName, TFsNotification::TFsNotificationType aType);
+
+	//Initialise iNotifyRequests and iStaticNotification
+	static void OpenL();
+	static TBool IsInitialised();
+	
+	/*
+	 * On CFsNotifyRequest closing, Close is called if this is the last request being removed.
+	 * This removes all of the managers private data.
+	 */
+	static void Close();
+
+	/*
+	 * Calls SetFilterRegister for every valid notification set in aMask.
+	 */
+	static void SetFilterRegisterMask(TUint aMask,TBool aAdd);
+	
+	/*
+	 * Adds or Removes to the count of filters set up for a particular type
+	 * This is a global count such that if there are no fiters for a particular type 
+	 * HandleChange doesn't need to do any iteration for that type.
+	 */
+	static void SetFilterRegister(TUint aFilter, TBool aAdd, TInt aCount = 1);
+	/*
+	 * Get the number of registers filters set up on a particular type.
+	 * @param aIndex the TFsNotificationType's index as determined from FsNotificationHelper::TypeToIndex
+	 */
+	static TInt& FilterRegister(TInt aIndex);
+
+	/*
+	 * Returns the number of CFsNotifyRequests set up
+	 */
+	static TInt Count();
+	
+	/*
+	 * Lock the iChainLock (currently not a ReadWriteLock)
+	 */
+	static void Lock();
+	
+	/*
+	 * Unlock iChainLock
+	 */
+	static void Unlock();
+
+private:
+  
+    /*
+     * @internalTechnology
+	 * Used by DoMatchFilter and DoHandleChange to control the flow of 
+	 * loop execution.
+     */
+    enum TFsNotificationFilterMatch
+        {
+        EDifferent  = 0x00, //Operation and Filters do not match.
+        EMatch      = 0x01, //Operation and Filters do match.
+        EContinue   = 0x02  //Data caged directory - Do not notify.
+        };
+    
+    /*
+     * Checks whether aOperation matches the filter name and/or path set in aFilter. 
+     */
+    static TFsNotificationFilterMatch DoMatchFilter(CFsClientMessageRequest* aRequest, const TDesC& aOperationName,CFsNotificationPathFilter& aFilter);
+    
+	/*
+	 * Iterates filters for a particular drive.
+	 * Called from HandleChange
+	 */
+	static void DoHandleChange(TFsNotificationTypeArray* aFilterTypeArray, TInt& aSeenFilter, CFsClientMessageRequest* aRequest, CFsNotifyRequest* aNotifyRequest, const TDesC& aOperationName, TFsNotification::TFsNotificationType& aType);
+	
+	/*
+	 * Stores the CFsNotifyRequests
+	 */
+	static CFsObjectCon* iNotifyRequests;
+	
+	//As we are doing notifications 'in-place' which is multi-threaded
+	//we need to have locking to protect iNotifyRequests.
+	//ToDo: ReadWriteLock
+	static RFastLock iChainLock;
+	
+	/*
+	 * Global register per filter type. 
+	 * Keeps a count of the number of filters set up for a particular type
+	 * (NB: EMediaChange is reported regardless of filters set)
+	 */
+	static TInt iFilterRegister[KNumRegisterableFilters];
+	
+	/*
+	 * This is a pool of blocks which are server-side versions of TFsNotification.
+	 * They are used so that we can have a single IPC from server to client.
+	 * 
+	 * it will also be used for coalescing changes.
+	 */
+	static CFsPool<CFsNotificationBlock>* iPool;
+	
+	friend class CFsNotifyRequest;
+	friend class RequestAllocator;
+	friend class TFsNotificationSubClose;
+	};
+
+
+#endif /* SF_NOTIFIER_H */
+
+