Sending strings in Wimp messages
Matthew Phillips (473) 721 posts |
With Wimp messages being limited to 256 bytes it can be a challenge if you’re devising a message protocol where strings need to be passed between applications. In the past when I’ve wanted to pass a couple of strings and not wanted to divide up the message space strictly between them I have had one of them start after the fixed-width material, e.g. at +56 or so, and the second string follow on immediately from that. This maximises the flexibility of the use of the space. On other occasions I have used a pair of words for each string: address and length. The receiving application has then been able to fetch the material using Wimp_TransferBlock. This technique requires there to be a reply to the message, so that the sending task knows when it can free the memory containing the strings. As I am constructing a new message protocol for some development work at the moment, I thought I’d explore to see how other messages handle this. The StrongHelp OS manual has a long page of User Messages under the Wimp section. The PlugIn protocol, devised for Acorn’s Browse, caught my eye. Many of the strings there are passed as a string_value, a word in the message which is either (if less than 256) an offset to the string held within the message itself, or (if greater than 256) an address in shared memory (e.g. RMA or a dynamic area). The manual notes that the protocol is designed so that there should always be a reply received, or the message bounced by the Wimp, allowing the sender to free the memory. This method is a bit more elegant than my two words method (address + length) as it requires fewer words of the message, allows for shorter strings to be in the message itself, and does not require memory to be paged in via Wimp_TransferBlock. But I’ve always written my applications to avoid use of RMA, and using a dynamic area for what will mostly still be fairly short strings seems like overkill. Besides the option of having the word either being an offset or a shared memory address, while elegant, complicates the programming at both ends, and cannot be generalised to binary data. I wonder whether there is a consensus on the “right” approach to use for this problem. Are there any other techniques people are aware of? (Other than transfer via a file!) |
Paolo Fabio Zaino (28) 1882 posts |
I do not represent a “consensus”, however for my DME Desktop Notifications component I am using the the method of sharing an address to an RMA area + area size and a bunch of metadata in the wimp message that any App can send to the Desktop Notification and that is because a notification may contain a URL, or a long message or even icon names and some macro that may define how the DME DNC has to message back the App in case an App requires for user-feedback to the notification. Please note: with “the DME DNC has to message back the App” I don’t mean the notification that the message from the App has been received (that is a must and as you mentioned is required so the App can release the memory). What the message back is is a new wimp message with the result of a user interaction (or a system interaction) with the DME DNC after the notification was delivered. Where delivered doesn’t necessarly means only on the screen to a user, because the DME DNC can also transform a notification into a SysLog message to be sent to a syslog server configured by the user or do other actions in consequence of a notifcation. So, in my particoualr case, there is no better way I am aware of and that also allow to (for example) expand the protocol in the future to include images or even JSON documents as part of a notification. Hope this helps, |
Dave Higton (1515) 3534 posts |
It seems to me that Wimp_TransferBlock represents the minimalist approach: there is no RAM consumed other than the source and the destination copy, and memory management is taken care of, as much as is possible – i.e. if the source memory is permanently allocated, there’s nothing to do, and if it’s not permanently allocated, no more than a DIM or malloc is required. |
nemo (145) 2554 posts |
Matthew asked
If the text will always fit in the message (e.g. a leafname) then define it to be so. Job done. If the text does not fit then it must be kept somewhere else for the duration of the message. This introduces some potential problems that are not well addressed by Wimp_SendMessage – a badly written program can cause a permanent memory leak in your program as we’ll see. As for the RMA, it’s not an intrinsically scary concept – for many APIs it is necessary anyway, and module tasks are likely to be using significant RMA workspace already. The real decider for RMA/AppSlot locality is “What purpose is the text put to and in what kind of message?”: If the text is a parameter to be passed to an OS API by address, then put it in the RMA to avoid further copying. If the message is broadcast, and used (or the string value inspected) by multiple tasks, then put it in the RMA to avoid all of those tasks having to initiate multiple task switches (TransferBlock) just to get a tiny bit of text. But if it’s being sent to just one recipient, and that one recipient will be wanting to keep a copy of that text then sure, TransferBlock is an option. But it’s more heavyweight than a string-copy to a little RMA buffer. In all cases the recipient knows it only has access to that string (by whatever mechanism) until it calls Wimp_Poll. However, if you rely on sending a broadcast or direct message “Recorded” so that you’re informed by the bounced message that all recipients have consumed the text and it can be discarded, be aware that any task that acknowledges the message will cause your task to never discard the text. One could ‘fire-and-forget’ and simply discard the data next time Wimp_Poll returns, but remember that broadcast messages are also delivered to the sender, perplexingly. Assuming your protocol only allows one such message in flight at a time (you don’t fire off three of them before calling Poll) then you might as well just keep a single RMA buffer hanging about, resize it when necessary, and discard it on Quit. That’s the lowest overhead. |
Rick Murray (539) 13850 posts |
The final idea is possibly the simplest to manage, but I’d change it slightly to be to claim a small Dynamic Area for your message buffer, and delete that on exit. That way you’re not dumping data into what really ought to be a system level area (but for historical reasons it’s a bit of a free for all), plus the area will carry a name so if the program crashes (or whatever), it can be either tidily removed or reclaimed the next time – both actions that can’t exactly be done with anonymous bits of RMA. Of course, programs will just work with an address saying “the buffer is here” so if you really need to support pre-RiscPC machines, you could use the RMA as a fallback (but note the issues of fragmentation). |
nemo (145) 2554 posts |
I don’t think this one minor protocol is the place to solve the node soup of the RMA. Get the rod out of thine own FontManager & MDF eyes. One small buffer per task is nothing, and unless you’re incontinently unable to keep your string within your buffer it’s not worth the hassle, IMO. |
Matthew Phillips (473) 721 posts |
Thank you, especially for the point about broadcasts and efficiency of all the switching. Some of the messages are indeed broadcast. I’ve managed to redesign my particular protocol so that only one string is required, and it will certainly fit in the message. |