Amiga.org
Operating System Specific Discussions => Amiga OS => Amiga OS -- Development => Topic started by: Jose on November 22, 2006, 03:34:02 PM
-
Hi. I'll 1st explain what I want to do. I have self propagating sequence of time intervals that are received in a MsgPort through PA_SoftInt or 3 set in the MsgPorts message arrival settings (mp_Flags). When a message is received I want to be able to change a forward linked list of structures wich have directory locks, wich are used by a process that records stuff to the HD according to received messages. There's no problem with race conditions, because the locks are changed/freed by the HD recorder process itself (a special message sent to it specifies what to change). However I'm having a problem sending the messages to it because I can't use any system memory allocation functions from an interrupt, hardware or software. I can however, according to the RKM Libs, use PutMsg() and Signal().
The solution I've invented is like this (I think it's pretty cool:)) Use a Semaphore to protect a BOOL variable or whatever piece of data that will serve as an arbitration. The Semaphore will be held constantly by another very small process that allocates memory and that's waiting for a signal to do it. The interrupt processing code will signal it to do it. Up to this point nothing will happend cause interrupts, even software ones, allways have an higher priority than tasks. Then the interrupt processing code will try to Obtain the said Semaphore exclusively, wich will of course make the memory allocating function work. After it's allocated the Memory to a shared memory address it relinquishes the Semaphore and the interrupt processing code can then use the allocated memory to send a message to the HD recorder process, wich frees the memory after processig the message.
I have 3 questions:
1- When the interrupt code loses the CPU when it ObtainSemaphore()s the system will be multitasking or only the small memory allocating process will execute ?
2- The RKM Libs has a list of functions that you can use from an interrupt and ObtainSemaphore is not one of them. This would make my solution not work, however the Semaphore system is documented to use signals and Signal() is a function you can use. I suppose the Semaphore lists could be inconsistent, but I wouldn't add the said semaphore to the system Semaphore list.
3- This is difficult, interesting, challenging and frustrating:lol: Is there a better way to achieve what I want to do ? Suggestions wanted/accepted.
-
You can't use semaphores from an interrupt (or anything that might call Wait()).
AllocMem() itself does not break forbid (or disable), so why not just call AllocMem directly?
-
Short and strong answer to a long post 8-) Thanks for taking the time. I actually knew you couldn't use Wait, even indirectly but had forgot about it...
I'll have to try to figure out another solution. I could allocate the memory beforehand but there's no guarantee as to how many interrupts can happen within a period of time (will depend on user settings).
-
"AllocMem() itself does not break forbid (or disable), so why not just call AllocMem directly?"
The RKM Libs says you can't use AllocMem cause the system memory lists might be inconsistent (the interrupt might have interrupted a system operation that was manipulating them and hadn't yet finished).
-
That would be true if it was an interrupt, but in this case it's not: The OS is inside PutMsg to your msgport, not AllocMem. At least I can't think of any condition in which the port could be PutMsg()d while the memory list is in inconsistent state. This at least with mp_Flags of 3.
PA_SOFTINT might be another story.
-
Aha! That's nice. I almost changed the code to use PA_SOFTINT instead cause with mp_Flags == 3 the system is in Disable, while PA_SOFTINT is just Forbid I think.
I suppose with PA_SOFTINT there's no easy way around to what I want to do ?
-
Well even though softint would break forbid (IIRC it doesn't), it still is quite unlikely that someone would send message to your msgport while memlist is inconsistent. So IMO PA_SOFTINT is pretty much as safe.
-
"Well even though softint would break forbid (IIRC it doesn't), it still is quite unlikely that someone would send message to your msgport while memlist is inconsistent. So IMO PA_SOFTINT is pretty much as safe."
I got various processes running, some of which can allocate memory to send messages to the HD recorder process very frequently, so if softint breaks forbid maybe it could happend.
Anyway, I'm tired, for now I'm just gonna keep 3 in mp_Flags (and use AllocMem/Vec). :-)
Cheers
-
I got various processes running, some of which can allocate memory to send messages to the HD recorder process very frequently, so if softint breaks forbid maybe it could happend.
And all memory allocation routines Forbid/Permit internally. How can them PutMsg inside memory allocation/free?
-
1. Calculate the maximum amount of mem your interrupt routine could ever possibly need.
2. Allocate that much mem for it at the very start of your program.
3. Problem solved.
p.s. Whether AllocMem() breaks Forbid() or Disable() or whatnot is completely irrelevant because one never knows how long an AllocMem() will take. After some days of running normal software (or just 1 single day of running a bunch of MUI software) I personally guarantee you that AllocMem() will take 3.8 forevers due to excessive memory fragmentation. If just one of your interrupts takes even half of a forever then your buffers will all overflow or underflow and your software will go kaplooie.
-
@ChaosLord
I'll pretend you never suggested that.
-
ChaosLord's suggestion does have a point: depending on how exactly mem is allocated/freed, fragmentation might become a problem. If you allocate everything needed in advance, there's nothing to worry about. And you'd produce less overhead (for realtime requirements) - as long as you can live with the memory usage.
Also, I can't really imagine AllocMem() working inside an interrupt...
-
It's no interrupt, that is: it can't be in the middle of memory allocation/free.
Using static size buffers that are "large enough (to certain point)" is rather silly IMO. It wastes memory and will fail if something needs more memory after all.
If you're worried about performance, you can use simple pooled allocation with Allocate/Deallocate.
-
Slightly off-topic
Does anybody have any performance statistics for the different memory allocation functions supported by exec?
I'm interested in the time taken to allocate/deallocate as a function of allocation size, for each method*
*I'm fully aware that this would likely change as memory becomes fragmented.
-
They're all based on Allocate()/Deallocate(), so they're quite fast while memory is not fragmented.
However, FreePooled() is a bit special: It gets really slow due to the puddle scanning to find the proper memory header to call Deallocate() on. This gets exponentally slow.
AmigaOS 3.9 BB2 fixed this by using the new AVL tree (http://en.wikipedia.org/wiki/AVL_tree) binary balanced tree instead of simple linked list for the puddles.
-
Presumably then, they all have at least O(N) cost as a function of the number of nodes in the free memory list?
-
In the worst case every 8 bytes is allocated and free. This way it's the (memory_area_size / 8 / 2) nodes to walk thru before you find the correct chunk.
At Allocate(), if the large enough free memory chunk is after such alteration of allocated and free chunks, it needs to walk thru all of them to find the large enough one.
At Deallocate() it needs to walk all free chunks until it finds chunk to merge with (or to insert after).
Needless to say, fragmentation is bad. If you need absolute speed, I suggest using object cache for the time critical things, or other similar methods.
-
I'm interested in all of this as I weigh up the cost of writing my own custom allocator routines versus a basic wrapper for the memory pool allocation scheme.