# new_dict

Definition: | integer tid = new_dict(sequence kd_pairs={}, integer pool_only=0) -- or -- integer tid = new_dict(string dictname="") -- or -- integer tid = new_dict(integer existing_tid) -- or -- sequence tids = new_dicts(integer n)
Description: | Creates a new dictionary, so that clashes with other uses of {key,data} pairings can be avoided, and optionally initialises it with a set of {key,data} pairs, or a diagnostic name, or makes a complete copy of some other already existing dictionary, or makes n empty dictionaries.
pwa/p2js: | Supported.
Comments: | If kd_pairs is a string then it is treated as a name for the dictionary, which may be useful for diagnostic purposes, and can be retrieved using dict_name(). Otherwise every element of kd_pairs, if present, should be a sequence of length 2, and said {key,data} gets passed to setd(), along with the id of the newly created dictionary. The returned tid is intended to be passed as the last parameter to setd(), getd(), deld(), destroy_dict(), traverse_dict(), and similar, taking care not to miss any. For smaller applications, there is a default dictionary, 1, that can be used without ever calling new_dict(), however larger applications and in particular third party components will obviously benefit from keeping their data private (by the simple act of not sharing their tid1). When you have no further use for it, an entire dictionary can be removed by invoking destroy_dict(tid), except for the default dictionary, 1, as mentioned above, which is special and is the only dictionary that cannot be deleted - attempts to do so merely empty it and leave it still available for use. Dictionaries are not thread-safe, for details of that, or if you are curious about the mysterious pool_only parameter, see the technicalia. The copy_tid = new_dict(existing_tid) is fast, performing only a few top-level reference count increments. Note however that it is only thread-safe if both copy_tid and existing_tid are only ever used by the same thread, otherwise locking is required around each and every call to any routine from dict.e that is passed either of those now-shared ids, and possibly around anything that uses the results from such calls (keys, values, and ref counts of said). It is also not possible to make a copy of an existing dictionary and give it a new diagnostic name, instead the copy just gets "".
Disclaimer: | 1 Rogue code that wantonly writes garbage to a tid of, say, 5, when it did not obtain that 5 from new_dict(), can obviously wreak havoc. Should it ever prove necessary it would probably be fairly easy to change integer tid to {integer tid, string passkey} just to make it far harder to accidentally replicate a tid.
Example 1: |
    integer tid = new_dict({{1,"one"},{2,"two"}})
Example 2: |
    -- multithreaded applications need to use locking (see Technicalia)
    integer dictcs = init_cs(), tid
        enter_cs(dictcs)
        tid = new_dict("thread data")
        leave_cs(dictcs)
See Also: | enter_cs, destroy_dict
Technicalia | Multiple threads concurrently accessing dictionaries will cause corruption unless protected as shown in Example 2. If your application is not multithreaded, or only one thread accesses dictionaries, then nothing that follows is of any concern to you, and if I were you, I’d get out while I still can. A single critical section protecting all dictionaries is normally sufficient, and in the vast majority of cases there would be no point making life any more difficult for yourself, however per-dictionary locks are also perfectly viable, and if only one thread has access to a particular dictionary, then only new_dict() and destroy_dict() may need locking - but for either of the last two options (ie per-dict and create/destroy-only-locking), then a big enough pool must be created at the get-go: If pool_only is greater than 1, the routine adds the specified number of entries to the freelist and returns 0. Such a call is of course expected to be made before any create_thread() or other new_dict() calls, and certainly before any destroy_dict() calls - you should verify the result actually is zero to guard against the latter. Subsequent invocations of new_dict(pool_only:=1) [presumably from within an appropriate critical section] will yield 0 if the pool is exhausted. In that case, the program should unlock, sleep and retry a bit later. You can actually specify a diagnostic name and pool_only of 1 (or 0), however passing a string plus pool_only>1 triggers a fatal error. If in fact you are trying to shut down, but other threads have died or forgotten to destroy_dict(), it is not entirely unreasonable (after a time) to offer a "create anyway" option - it is not actually very likely, let alone guaranteed to fail - unless some other thread is beavering away making as many calls to builtins\dict.e routines as it can. I only mention this because systems which are multithreaded and require many dictionaries are somewhat more likely to have complex and critical shutdown requirements, and stand to lose much more data if terminated abruptly. Tip: it costs very little to create a pool of say 500 dicts, and any such overhead would be completely and utterly dwarfed by trying to create 500 threads. Conversely, limiting the pool size may keep memory useage spikes down and otherwise help spread out the workload. Alternatively you can create as many(/all) dictionaries as you need up-front, before creating any threads, and hence not need any locking, and farm them out to the threads as you create them, in which case you have implicitly created a large enough pool, assuming there won’t be any more new_dict() calls further down the line or occurring in other unrelated threads. Likewise should each thread invoke destroy_dict() that would still need locking, but if you wait for them all to finish, then you can delete them all in one go without any locking - again assuming there are no other threads using dictionaries still running. Not that there’s any harm or it isn’t simpler to lock around bulk deletion, but note that locking around bulk creation would be insufficient if the pool wasn’t already large enough beforehand. Let me reiterate to make all this as clear as possible: If the program is not multithreaded, or only one thread accesses dictionaries, no locking required, go away now. If multiple threads might access the same dictionary, locks are required around every call. In the latter case you can have one lock for all dictionaries or one lock per dictionary (see last two points). If a dictionary is only ever accessed by one thread, you can limit locking to new_dict/destroy_dict, but you must work within a limited-size pool of dictionaries (or lock all calls). Likewise any use of per-dictionary locks mandates the use of a limited-size pool of dictionaries. The locks around new_dict/destroy_dict calls must always of course be system-wide, not per-dictionary.