OS_ChangeDynamicArea - returned value?
Simon Willcocks (1499) 513 posts |
The documentation says that on entry, R1 is “Amount to move in bytes (signed integer)”, but on exit R1 is “Amount the area has changed in bytes (unsigned)”. Either areas can’t shrink (they can), or the exit description is wrong. Is it: https://www.riscosopen.org/wiki/documentation/show/OS_ChangeDynamicArea |
Jeffrey Lee (213) 6048 posts |
Option C. I believe the behaviour is effectively as follows: _kernel_oserror *os_changedynamicarea(_kernel_swi_regs *r) { _kernel_oserror *err = claim_cda_semaphore(); if (err) { r->r[1] = 0; return err; } err = check_da_valid(r->r[0]); if (err) { release_cda_semaphore(); r->r[1] = 0; return err; } bool shrink = (r->r[1] < 0); unsigned int pages = abs(r->r[1]) / page_size; unsigned int moved = 0; if (shrink) { while (pages-- && !err) { if ((err = try_remove_page_from_area(r->[0])) == NULL) moved++; } } else { while (pages-- && !err) { if ((err = try_add_page_to_area(r->[0])) == NULL) moved++; } } r->r[1] = moved * page_size; release_cda_semaphore(); return err; } Although the real version has more logic to trigger service calls to notify other components of the changes, logic for claiming spare pages from application space if a page is needed but the free pool is empty, etc. All the error cases where no memory is moved should result in R1 being zero on exit. (note: post edited a couple of times to correct errors!) |
Simon Willcocks (1499) 513 posts |
This doesn’t look right: unsigned int pages = abs(r->r[1]) / page_size; increasing by less than page_size wouldn’t change the size, and decreasing by one would take out a whole page. But the important thing is that on exit R1 contains a multiple of page_size, which is the number of bytes added or removed from the DA? Whacky, but I get it now. The readable size of the DA is always a multiple of page_size, I see, so adding one byte always adds one page. Interesting. Thanks! |
Jeffrey Lee (213) 6048 posts |
We’re both half right. Growing by one byte should add a page. Shrinking by one byte should do nothing. (Confirmed by experimentation this time, rather than trusting myself to read the source correctly!) if (shrink) pages = (-r->r[1]) / page_size; else pages = (r->r[1] + page_size-1) / page_size;
No. Also note that internally the size is always tracked in page units, so shrinking by one byte 4096 times in a row still does nothing. |
Simon Willcocks (1499) 513 posts |
You’re right, of course. My code’s similar. int32_t resize_by_pages = resize_by >> 12; if (resize_by == 0) { // Doing nothing return true; // Not the same function definition! } if (0 != (resize_by & 0xfff)) { if (resize_by > 0) resize_by_pages ++; else resize_by_pages --; } |
Stuart Swales (8827) 1357 posts |
But that will get the -1 case wrong. Surely its |
Rick Murray (539) 13840 posts |
It was always thus with such system-level memory allocations. Whether Font Cache, Wimp Slot, RAMdisc, or whatever – it always worked in units of the page size. Imagine – MEMC1a on a 4MB machine, the page size was 32K. So you didn’t have a lot of memory, and the allocation unit was quite large (and thus likely wasteful). |
Simon Willcocks (1499) 513 posts |
Ah, you spotted my deliberate mistake! Ahem. 3 fewer lines of code! |