diff -r 43e37759235e -r 51a74ef9ed63 Symbian3/SDK/Source/GUID-FE910347-7CC1-5241-B443-88AD3F5A96EF.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Symbian3/SDK/Source/GUID-FE910347-7CC1-5241-B443-88AD3F5A96EF.dita Wed Mar 31 11:11:55 2010 +0100 @@ -0,0 +1,422 @@ + + + + + +Using +Publish and SubscribeThis topic explains the operations that can be performed using +publish and subscribe. + +
Creating and +closing a handle to a property

Some property operations require +a reference to the property to be established first. This is done using the RProperty::Attach() member +function. After a call to this function, the RProperty object +acts like a standard handle to a kernel resource. When this handle is no longer +required, it can be released in the standard way by calling the inherited RHandleBase::Close() member +function.

Note that releasing the handle does not cause the property +to disappear. This only happens if the property is deleted.

Note also +that it is quite legitimate to attach to a property that has not been defined, +and in this case no error will be returned either. This enables the lazy definition +of properties as used in some of the usage patterns.

// attach to the ‘counter’ property +RProperty counter; +TInt r=counter.Attach(KMyPropertyCat,EMyPropertyName,EOwnerThread); +User::LeaveIfError(r); + +// use the counter object... + +// when finished, release the handle +counter.Close(); +
+
Defining a +property

A property is defined using the RProperty::Define() function, +specifying the attributes of that property.

A property does not need +to be defined before it can be accessed. This supports programming patterns +where both publishers and subscribers may define the property. Note, however +that for security reasons, there are restrictions on the category that can +be used when defining a property; see security +issues for more information.

Once defined, a property persists +in the kernel until the system reboots, or the property is explicitly deleted. +Its lifetime is not tied to that of the thread or process that originally +defined it. This means that, when defining a property, it is important to +check the return code from the call RProperty::Define() to +deal with the possibility that the property has previously been defined, but +not deleted.

The following code shows the definition of two properties:

enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +static _LIT_SECURITY_POLICY_PASS(KAllowAllPolicy); +static _LIT_SECURITY_POLICY_C1(KPowerMgmtPolicy,ECapabilityPowerMgmt); + +// define first property to be integer type +TInt r=RProperty::Define(EMyPropertyCounter,RProperty::EInt,KAllowAllPolicy,KPowerMgmtPolicy); +if (r!=KErrAlreadyExists) + { + User::LeaveIfError(r); + } + +// define second property to be a byte array, allocating 100 bytes +r=RProperty::Define(EMyPropertyName,RProperty::EByteArray,KAllowAllPolicy,KPowerMgmtPolicy,100); +if (r!=KErrAlreadyExists) + { + User::LeaveIfError(r); + } +. . . +

Once defined, a property value can change, but the property +type cannot. Byte-array type properties can also change length provided the +length does not exceed the maximum value of 512 bytes. The limit on the size +of a property ensures some limit on RAM usage.

The API allows byte-array +and Unicode text type properties to be pre-allocated when they are defined. +This means that the time taken to set the values is bounded. However, if the +length of these property types subsequently increases, then memory allocation +may take place, and no guarantees can then be made on the time taken to set +them.

There are further security +issues to be considered when defining a property. You need to provide +two security policies - one to govern which processes can publish the property +value, and the other to govern which processes can retrieve the property value. +Security policies are instances of TSecurityPolicy objects, +although for efficiency reasons, you will almost always use the _LIT_SECURITY_POLICY_... macros +to generate constant objects that behave like TSecurityPolicy objects. +The API reference for TSecurityPolicy provides far more +detail on this.

In this example, all processes will be allowed to +retrieve the property value, but only those processes having the power management +system capability (ECapabilityPowerMgmt) will be allowed +to publish the property value.

Note that a process that defines a +property does not have automatic rights of access to that property, other +than to delete it. If the defining process also wishes to publish and/or subscribe +to that property, then it must ensure that it satisfies the security policies +that it itself has put in place when defining the property.

+
Deleting a +property

A defined property is deleted by calling RProperty::Delete(). +Any outstanding subscriptions for this property will complete with KErrNotFound. +Only the process with the correct secure ID is allowed to delete it.

For +example, extending the code fragment introduced above:

enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +// define first property to be integer type +TInt r=RProperty::Define(EMyPropertyCounter,RProperty::EInt); +if (r!=KErrAlreadyExists) + { + User::LeaveIfError(r); + } + +// define second property to be a byte array, allocating 100 bytes +r=RProperty::Define(EMyPropertyName,RProperty::EByteArray,100); +if (r!=KErrAlreadyExists) + { + User::LeaveIfError(r); + } +. . . + +// much later on + +. . . +// delete the ‘name’ property +r=RProperty::Delete(EMyPropertyName); +if (r!=KErrNotFound) + { + User::LeaveIfError(r); + } +
+
Publishing +a property value

A property is published using the RProperty::Set() family +of functions. Properties can be published:

    +
  • using a previously attached RProperty handle,

  • +

or

    +
  • by specifying the property +category and key with the new value.

  • +

The former mode is guaranteed to have bounded execution time, suitable +for high-priority, real-time tasks, except when publishing a byte-array property +that requires the allocation of a larger space for the new value.

The +latter mode offers no real-time guarantees.

Property values are written +atomically. This means that it is not possible for threads reading a property +to get a garbled value.

All outstanding subscriptions for a property +are completed when the value is published, even if it is exactly the same +as the existing value. This means that a property can be used as a simple +broadcast notification service.

Publishing a property that is not +defined is not necessarily a programming error. The Set() functions +just return an error. If this is not expected for any particular usage, then +the error must be checked and processed by the caller.

See the code +fragment in the section Retrieving +a property value

+
Retrieving +a property value

The current value of a property is read using +the RProperty::Get() family of functions. Properties can +be retrieved:

    +
  • using a previously attached RProperty handle,

  • +

or

    +
  • by specifying the property +category and key with the new value.

  • +

The former mode is guaranteed to have bounded execution time, suitable +for high-priority, real-time tasks.

The latter mode offers no real-time +guarantees.

Property values are read atomically. This means that it +is not possible for threads reading a property to get a garbled value.

Retrieving +a property that is not defined is not necessarily a programming error. The Get() functions +just return an error. If this is not expected for any particular usage, then +the error must be checked and processed by the caller.

Integer properties +must be accessed using the overloads that take an integer or integer reference +value, whereas byte-array properties can be accessed using the overloads that +take a descriptor reference.

The following code fragment shows publication +and retrieval of a property. Note that it uses the idea of attaching to a +property. Note also that it contains a race condition, especially if another +thread is executing the same sequence to increment the ‘counter’ value.

const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +// attach to the ‘counter’ property +RProperty counter; +TInt r=counter.Attach(KMyPropertyCat,EMyPropertyCounter,EOwnerThread); +User::LeaveIfError(r); + +// publish a new name value +TFileName n; +RProcess().Filename(n); +r=RProperty::Set(KMyPropertyCat,EMyPropertyName,n); +User::LeaveIfError(r); + +// retrieve the first 10 characters of the name value +TBuf<10> name; +r=RProperty::Get(KMyPropertyCat,EMyPropertyName,name); +if (r!=KErrOverflow) + { + User::LeaveIfError(r); + } + +// retrieve and publish a new value using the attached ‘counter’ property +TInt count; +r=counter.Get(count); +if (r==KErrNone) + { + r=counter.Set(++count); + } +User::LeaveIfError(r); + +// when finised, release the handle +counter.Close(); +
+
Subscribing +to, and unsubscribing from, a property

Subscribing to a property +is the act of making an asynchronous request to be notified of a change to +that property.

A thread makes a request for notification of a change +to a property by calling the RProperty::Subscribe() member +function on an already attached property object. Only one subscription request +can be outstanding at any one time for a RProperty instance.

An +outstanding subscription request can be cancelled by calling the RProperty::Cancel() member +function. This is unsubscribing from the property.

Subscribing to +a property is a single request to be notified when the property is next updated, +it does not generate an ongoing sequence of notifications for every change +to that property's value. Neither does it provide the caller with the new +value. In essence, the act of notification should be interpreted as “Property +X has changed” rather than “Property X has changed to Y”. This means that +the new value must be explicitly retrieved, if required. As a result, multiple +updates may be collapsed into one notification, and subscribers may not have +visibility of all intermediate values.

This might appear to introduce +a window of opportunity for a subscriber to be out of synchronisation with +the property value – in particular, if the property is updated again before +the subscriber thread has had the chance to process the original notification. +However, a simple programming pattern, outlined in the second example below +ensures this does not happen. The principle is that, before dealing with a +subscription completion event, an active object should re-issue the subscription +request.

Note that if the property has not been defined, then a subscription +request does not complete until the property is subsequently defined and published. +Note that the request will complete with KErrPermissionDenied if +the subscribing process does not have sufficient capability as defined by +the TSecurityPolicy object supplied by the process defining +the property.

If the property is already defined, then the request +completes immediately with KErrPermissionDenied if the +subscribing process does not have sufficient capability.

const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +// attach to the ‘counter’ property +RProperty counter; +TInt r=counter.Attach(KMyPropertyCat,EMyPropertyCounter,EOwnerThread); +User::LeaveIfError(r); + +// wait for the previously attached ‘counter’ property to be updated +TRequestStatus s; +counter.Subscribe(s); +User::WaitForRequest(s); + +// Notification complete, retrieve the counter value. +TInt count; +counter.Get(count); +. . . + const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +// Active object that tracks changes to the ‘name’ property +class CPropertyWatch : public CActive + { + enum {EPriority=0}; +public: + static CPropertyWatch* NewL(); +private: + CPropertyWatch(); + void ConstructL(); + ~CPropertyWatch(); + void RunL(); + void DoCancel(); +private: + RProperty iProperty; + }; + +CPropertyWatch* CPropertyWatch::NewL() + { + CPropertyWatch* me=new(ELeave) CPropertyWatch; + CleanupStack::PushL(me); + me->ConstructL(); + CleanupStack::Pop(me); + return me; + } + +CPropertyWatch::CPropertyWatch() + :CActive(EPriority) + {} + +void CPropertyWatch::ConstructL() + { + User::LeaveIfError(iProperty.Attach(KMyPropertyCat,KMyPropertyName)); + CActiveScheduler::Add(this); + // initial subscription and process current property value + RunL(); + } + +CPropertyWatch::~CPropertyWatch() + { + Cancel(); + iProperty.Close(); + } + +void CPropertyWatch::DoCancel() + { + iProperty.Cancel(); + } + +void CPropertyWatch::RunL() + { + // resubscribe before processing new value to prevent missing updates + iProperty.Subscribe(iStatus); + SetActive(); + + // property updated, get new value + TFileName n; + if (iProperty.Get(n)==KErrNotFound) + { + // property deleted, do necessary actions here... + NameDeleted(); + } + else + { + // use new value ... + NameChanged(n); + } + } +
+
Usage patterns

There +are three usage patterns that can easily be identified, labelled as: standard +state, pure event distribution, and speculative publishing.

Standard state

This +pattern is used for events and state that are known to be used widely in the +system. Examples of this might be battery level and signal strength, which +are important in every phone.

The publisher calls RProperty::Define() to +create the appropriate property. For byte array or text properties, a size +sufficient for all possible values should be reserved. An error of KErrAlreadyExists should +be ignored. The publisher then publishes the property values as, and when, +appropriate. If the RProperty::Set() call fails, this should +be treated as a serious error, since it indicates that important system state +is not getting through. Appropriate action might include panicking or rebooting +the system. Subscribers will use RProperty::Subscribe() to +request notification, and RProperty::Get() to retrieve the +property value on notification.

The memory to store the property value +will be permanently allocated, even if it turns out that no-one in the system +needs that value. This does ensure that the value can always be published, +even if the system is in an out of memory situation. For this reason, this +approach should be limited to widely used and important state. The Speculative publishing pattern offers an approach for dealing with +less important state.

Pure event distribution

This +pattern is used when events need to be distributed, not values.

The +publisher of the event simply uses an integer property, and calls RProperty::Set() +with any value. Even if the value of the property is not changed by this operation, +all subscribers will be notified that a Set() has occurred, and by implication +that the related event has occurred.

Subscribers will be able to detect +that an event has occurred, but will get no other information. The minimum +possible memory is wasted on storage for the dummy value.

Speculative publishing

This +pattern is used when it is not known whether a value will be of interest to +others or not. Unlike the standard +state pattern, the publisher of the event does not call RProperty::Define() to +create the property. Instead, it simply calls RProperty::Set() as +appropriate, and ignores any KErrNotFound error.

When +other code in the system, i.e. a potential subscriber, is interested in the +state, it calls RProperty::Define() to create the property +and allocate the memory for the value. An error of KErrAlreadyExists should +be ignored, as this only indicates that some other code in the system is also +interested in the value and has already created the property.

The +subscriber then calls RProperty::Subscribe() and RProperty::Get() as +usual to interact with the property. On the first Get(), +the subscriber may retrieve the property default value (zero, or a zero length +descriptor). This must be substituted with a sensible default value for the +property in question.

Using this pattern, no memory is wasted on properties +that have no subscribers, while the publisher code is simpler as there is +no need for configuration as to which properties to publish.

The publisher, +however, wastes some time attempting to publish unneeded values, but this +should not be an issue unless the value is very frequently updated.

Where +events are published very infrequently, the subscriber could have a dummy +value for a long time, until the next publish event updates the value. Often +this is not a problem as a default value can be substituted. For example a +full/empty indicator for a battery level, none for signal strength etc. This +pattern is unlikely to be useful if there is no suitable default value.

+
Making efficient +use of the user side API

While the Publish and Subscribe API, as +represented by RProperty is designed to be efficient, there +are certain usage patterns that can improve performance.

Use +the attached version of the API calls if possible

If you intend +to call Set() or Get() repeatedly, it is +preferable to use the attached forms of the calls. The attached forms are +the ones that do not take a UID/Key parameter, and that can only be called +after calling RProperty::Attach(). The attached variants +are constant time operations, and execute much faster than the corresponding +unattached versions.

Only +preallocate space if the publishing is time critical

For byte-array +and text properties, it is possible to pre-allocate space for the data. Doing +this results in Set() operations that do not exceed the preallocated +space, avoiding the need to do a memory allocation. However, if the data is +shorter than the reserved space, the excess is wasted. Since Set() automatically +extends the data area if needed, then the only reason to pre-allocate space +is if the Set() operation has to be real-time, i.e. has to +have known execution time.

Always +consider using Speculative Publishing

Even in situations where +the Standard +State pattern may seem appropriate, speculative publishing may be a +better choice, specially for low-level components that know little about how +they are used or what the wider system configuration may be. The onus is then +on the UI/Policy layer to ensure that the appropriate properties are defined +early on in device boot according to policy rules it can define. This ensures +that the policy layers in the system maintain control and can implement a +wide variety of policies.

Standard state is only relevant for properties +that are essential to every Symbian device. Battery level probably falls into +this category, signal strength may well not.

Define the expected update frequency

When a property is changed, +all subscribers are notified. This leads to their threads running to service +the notification. If a property changes value frequently, it would be wasteful +for subscribers to perform substantial processing for each notification.

Take +a property representing signal strength as an example. Potentially, this could +be updated several times a second. If a change in value were only used to +update the UI signal bar, it would not be harmful. However, if it were used +by many entities for serious processing (e.g. polling for email, sending unsent +SMSes, re-connecting to the internet), then such frequent updates would have +a severe effect on battery life.

Nevertheless, it is obviously desirable +for many parts of a phone OS to know about the state of network coverage, +and to take appropriate action. In cases like this, it may be worth the publisher +defining multiple properties with associated update characteristics. For example, +raw signal strength (updated > 1 time/sec), periodic signal strength (updated +once every 10s) and network coverage (updated only when moving between some +signal and none). Each subscriber can then monitor the appropriate notification +and so reduce the number of threads that run when the underlying value changes.

+
\ No newline at end of file