If you've got a Palm OS device with current versions of NVFS, you should be very careful about exhausting the storage available for databases on the device. DmSyncDatabase can fail in an unexpected way.
First, lets give some background on how NVFS works. The current implementation is a layer that hooks into the Data Manager and lets databases live on a VFS volume. Usually, this is a hidden internal volume, rather than a memory card card that can be removed by the user. At this time, NVFS is used on three palmOne devices: the Tungsten T5, the Treo 650, and the Tungsten E2.
The current setup has the devices RAM divided into three regions. All of these devices have 32MB of DRAM. 16MB is used to store a copy of the system ROM which is loaded from NAND Flash at system boot time. While this region is in RAM, it's protected memory and cannot be written after boot. There's a second region, around 5MB, that's used for the dynamic heap. All of your MemPtrNew allocations come from this area. Finally, the remaining 11MB is used for a DB cache. This is protected memory that can be written using DmWrite. Allocations from FtrPtrNew are done here, and copies of records from your databases are stored here temporarily while they're in use.
The current NVFS implementation is a layer under Data Manager. These are all the DmWhatever functions that are used to read and write database records. When a database is opened, you can now read in records into the cache using DmGetResource, DmQueryRecord, or DmGetRecord.
Records read with DmGetResource and DmGetRecord will stay in the cache until released by the appropriate DmReleaseResource/DmReleaseRecord call. In the current implementations, resources will stay in the cache until the resource DB is closed, but data records can be purged if space is needed.
Unfortunately, when Palm OS was originally implemented, DmQueryRecord didn't have an accompanying "unquery" call, so you need to requery a record before every use to make sure that it is in memory. If you follow the pattern of Query/Lock/Unlock, you should be OK.
So, to modify a record, you should use DmGetRecord/MemHandleLock/DmWrite/MemHandleUnlock/DmReleaseRecord with dirty flag set. If you don't set the dirty flag, it's likely that your change to the record will never get flushed back out the storage volume.
You also need to make sure that your DB is opened for writing. NVFS will not flush changes to a database that's opened as read-only. Several big applications have made this mistake, but until NVFS, the system did not detect this error.
Databases are written back to flash memory on several occasions. First, a database is written when it is closed. Second, they are written when the user puts the device into sleep mode. Third, they are written on a call to DmSync or DmSyncDatabase. A soft reset of the device will not commit pending changes.
Syncing of a database back to storage can fail. Changes are committed to the database files using a write-then-modify strategy. The changed records are either appended to the end of the database file or written into holes in the middle of the file where previous records lived. After all the changes are written to the file, the file's internal tables are modified to point to the new data. Finally, any records that were replaced are marked as free so future writes will go to those holes before expanding the file further. If there's not enough room on the volume to write the changes, NVFS throws away all of the modifications, leaving the database in the state it was at the last sync. This means that you need to write your code such that anytime the user has control of the device (you return to the EvtGetEvent loop), all DB writes that are needed to give your DB a consistent state have been performed. This is because the user could put the device to sleep, which would flush the writes, and if the device was reset at that time, you want your databases to be valid.
This doesn't mean that you have to explicitly sync all of the time. However, it's important to sync after you've done something that destroys data elsewhere. For example, if you're pulling down email from a server, do a sync and make sure it's written to your own memory before sending the delete command. You also need to be sure to check the return value of DmSyncDatabase; my next post will describe how to do that correctly, since the original implementation of DmSyncDatabase returned the value in the wrong 68K register.
