New command "IsMemoryID(*memoryID)

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 666
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

New command "IsMemoryID(*memoryID)

Post by Kurzer »

This may be a stupid suggestion, but I have a use for it.

I miss a IsMemoryID() function to check if a memory ID is still valid.
Unfortunately, it is not possible to use MemorySize(*MemoryID) for this check, because the program will crash if the MemoryID is not valid.

In my case, a procedure allocates memory independently and returns the MemoryID as a result. The calling process must release this memory on its own responsibility.

Since these functions are implemented in an OOP-framework (thanks to mk-soft), I would like to check this allocated memory and release it myself if necessary (because the user forgot to free the memory) when the related object will be destroyed.

Unfortunately this is only possible if I could check an invalid MemoryID without crashing the program.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: New command "IsMemoryID(*memoryID)

Post by Dude »

kurzer wrote:a IsMemoryID() function to check if a memory ID is still valid
A memory ID is just a pointer to a given address in RAM. It's always going to be valid, even if that memory is freed.
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: New command "IsMemoryID(*memoryID)

Post by Shield »

Dude wrote:
kurzer wrote:a IsMemoryID() function to check if a memory ID is still valid
A memory ID is just a pointer to a given address in RAM. It's always going to be valid, even if that memory is freed.
While this is true that the pointer is still valid (since it is just a regular integer variable), dereferencing (i.e. accessing) the memory certainly isn't.

So @kurzer, memory ownership has always been an issue that has been a big problem in simpler languages such as PB and C because the compiler
doesn't offer any support for it. I'd recommend that you use one of two ways:

1) If you allocate the memory (e.g. in a library you wrote), then you also provide a way to free it.
2) Generally, I prefer it the other way around where the user takes the responsibility and passes a block of memory to your function.
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: New command "IsMemoryID(*memoryID)

Post by freak »

Memory addresses can be re-used for new allocations once they have been freed. So even if the proposed IsMemoryID() function says that the memory is valid, you still don't know whether you can free it because there is no way to find out if it still references the same thing you once allocated. You could be freeing something that is still in use and have a random crash later.
quidquid Latine dictum sit altum videtur
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: New command "IsMemoryID(*memoryID)

Post by Josh »

The safest way is to have only one memory pointer and set it to 0 when you release the memory.
sorry for my bad english
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 666
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: New command "IsMemoryID(*memoryID)

Post by Kurzer »

Shield wrote:1) If you allocate the memory (e. g. in a library you wrote), then you also provide a way to free it.
2) Generally, I prefer it the other way around where the user takes the responsibility and passes a block of memory to your function.
Hello Shield,
1) is the way I am going now. I also offer my own FreeMemory function in my object and store and track the memory address internally to check if the memory has already been released via my function.

2) I can't do it like that, because the user doesn't know how big the memory has to be. This is only decided within the function that allocates the memory. The function may have to perform a ReAllocate() several times. This depends on the data it have to process.
Josh wrote:The safest way is to have only one memory pointer and set it to 0 when you release the memory.
Yes, thats the way I do it now internally in my object, but this does not prevent the user from ignoring my FreeMemory() function and try to release the memory on his own.
freak wrote:Memory addresses can be re-used for new allocations once they have been freed. So even if the proposed IsMemoryID() function says that the memory is valid, you still don't know whether you can free it because there is no way to find out if it still references the same thing you once allocated. You could be freeing something that is still in use and have a random crash later.
@Freak, okay, I understand the dilemma.
However, I wonder why Fred didn't implemented the handling like with other PureBasic objects (e. g. Gadget).

For example, if you use ButtonGadget (#GadgetNr, x, y, width, heigth, text$[, flags]) you can either assign a unique gadget number yourself or use #PB_Any. Then the command itself returns a unique gadget number.

With this number you can perform an IsGadget() query without any problems.

So why not

Code: Select all

Allocatememory (#MemoryNr, Size)
?

Assuming you are using Allocatememory (1,1024), the command could immediately return the memory address as a result (because you delivered the unique memory number by itself -> 1).
If you use Allocatememory (#PB_Any, 1024), the command would return the unique memory number and a command

Code: Select all

MemoryAddress (MemoryNumber)
would be needed to get the real memory address.

This would be close to the logical structure of the other PureBasic objects and therefore an

Code: Select all

IsMemory (memory number)
command would also be possible without any problems.

PS: I have thought about it several times and now I have to add the following information to my text above:

The problem with the ambiguous *MemoryID or MemoryNumber is only solved if AllocateMemeory() always returns a MemoryNumber by itself. With AllocateMemory(MemoryNumber, Size) only #PB_Any should be used as MemoryNumber (you could omit the first parameter, but the function must only return the MemoryNumber). PureBasic must then ensure that a new, previously not used MemoryNumber is always generated until the end of the program. The best way to do this is to always increase the memory numbers by one after every AllocateMemory() or FreeMemory() call until the exe quits.

Code: Select all

MyMem = Allocatememory(1024)  ; returns the MemoryNumber 1 for example
Debug MyMem ; prints 1
Debug MemoryAddress(MyMem) ; returns for example $3674861, the real address of the memoryblock
Debug IsMemory(MyMem) ; will return #True
Freememeory(MyMem) ; frees the memory at address $3674861 (could return #True to show memory was successfully freed)
Debug IsMemory(MyMem) ; will return #False

MyMem = Allocatemomory(512)  ; returns the MemoryNumber 2
MyMem2 = Allocatememory(1024)  ; returns the MemoryNumber 3
MyNewMem2 = ReAllocatememory(MyMem2, 4096)  ; still returns the MemoryNumber 3

Freememory(MyMem) ; could return #True to show memory was successfully freed

Debug IsMemory(MyMem) ; will return #False
Debug IsMemory(MyMem2) ; will return #True
Debug IsMemory(MyNewMem2) ; will return #True
Freememory(MyMem) ; could return #False to show memory number is not longer valid
Freememory(MyNewMem) ; frees the memory (could return #True to show memory was successfully freed
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: New command "IsMemoryID(*memoryID)

Post by freak »

It worked like this once upon a time. But it was much too cumbersome for simple memory allocations which is why it was changed.
kurzer wrote:For example, if you use ButtonGadget (#GadgetNr, x, y, width, heigth, text$[, flags]) you can either assign a unique gadget number yourself or use #PB_Any. Then the command itself returns a unique gadget number.

With this number you can perform an IsGadget() query without any problems.
This example has the same problem: If IsGadget() tells you that the gadget is valid you can't just call FreeGadget() on it without knowing if the gadget is still the one you created. Remember: Your use case is that somebody else might have already called FreeGadget() on it (and a new one could have taken the same ID). #PB_Any numbers can be re-used as well!

How is this for a solution:
You say the use case is an Object that creates the memory and hands it out as a result which the user can free or the object should free on cleanup. So why not also provide a dedicated "Free"-function on the same object that the user must call to free the memory? Your object can then manage an internal list of memory blocks it handed out and remove any block that was freed with the "Free"-function. Then you know exactly which memory blocks must still be freed at the end. No more guessing involved. This solution would also make your implementation more flexible. Since both creation and freeing is handled within you object, you can change the implementation (for example switch the memory alloc with a linked list or array) without any need to change the code outside the object.
quidquid Latine dictum sit altum videtur
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 666
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: New command "IsMemoryID(*memoryID)

Post by Kurzer »

that's exactly how its working now, but what prevents the user from freeing the memory at his own using FreeMemory() instead of my free memory function? In this case the FreeMemory() within the destroy method of my object would crash the programm.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: New command "IsMemoryID(*memoryID)

Post by Dude »

@Freak: A quick question regarding #PB_Any with gadgets: will #PB_Any ever create a gadget number that I might have used for another gadget? Or does it check if the #PB_Any number is already in use for a gadget? Thanks.
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: New command "IsMemoryID(*memoryID)

Post by Josh »

Dude wrote:@Freak: A quick question regarding #PB_Any with gadgets: will #PB_Any ever create a gadget number that I might have used for another gadget? Or does it check if the #PB_Any number is already in use for a gadget? Thanks.
The gadget number you receive by creating a gadget with #PB_Any is the Pb intern handle for the gadget. That means, its a pointer to the memory, where Pb stores informations for this gadget. So I think, it can't be guaranteed, that this gadget number is used again.
sorry for my bad english
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: New command "IsMemoryID(*memoryID)

Post by freak »

kurzer wrote:that's exactly how its working now, but what prevents the user from freeing the memory at his own using FreeMemory() instead of my free memory function? In this case the FreeMemory() within the destroy method of my object would crash the programm.
So? That would be a bug by the user then. What prevents the user from making some other mistake that is not related to your object and crashing the program that way?
Dude wrote:@Freak: A quick question regarding #PB_Any with gadgets: will #PB_Any ever create a gadget number that I might have used for another gadget? Or does it check if the #PB_Any number is already in use for a gadget? Thanks.
As long as your gadget exists the number will not be reused. But once you free the gadget the number can be used again for another gadget.
quidquid Latine dictum sit altum videtur
walbus
Addict
Addict
Posts: 929
Joined: Sat Mar 02, 2013 9:17 am

Re: New command "IsMemoryID(*memoryID)

Post by walbus »

Therefore I use #PB_any whenever possible.
It works perfectly and undesirable collisions are safely avoided.
There cannot be a duplicate ID #PB_any because they are addresses.
Just as you can never get two identical addresses with AllocateMemory
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 666
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: New command "IsMemoryID(*memoryID)

Post by Kurzer »

walbus wrote:Therefore I use #PB_any whenever possible.
It works perfectly and undesirable collisions are safely avoided.
There cannot be a duplicate ID #PB_any because they are addresses.
Just as you can never get two identical addresses with AllocateMemory
Thats correct, but unfortunately you didn't understand what kind of problem I was referring to with my request. I didn't say there were address collisions.
freak wrote:So? That would be a bug by the user then. What prevents the user from making some other mistake that is not related to your object and crashing the program that way?
Nothing, but regarding to the "memory freed twice problem" PureBasic itself could prevent a crash like I already suggested. But as you already said, it was implemented once upon a time and Fred decided to remove this feature, so my feature request is useless at this point - c'est la vie. ;-)
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
walbus
Addict
Addict
Posts: 929
Joined: Sat Mar 02, 2013 9:17 am

Re: New command "IsMemoryID(*memoryID)

Post by walbus »

There can't be any IsMemoryID() function, because you can and must release simple and very fast the memory with FreeMemory() and then write it to Nirvana

Such a function therefore makes no sense
User avatar
mk-soft
Always Here
Always Here
Posts: 5405
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: New command "IsMemoryID(*memoryID)

Post by mk-soft »

You can build a own Memory functions

Code: Select all

;-TOP
;
; Memory Debugging v0.4

CompilerIf #PB_Compiler_Debugger
  
  #MemoryStop = 1
  
  Global NewMap MemID()
  
  Procedure MyAllocateMemory(Size, Flags, Proc.s)
    Protected *mem
    *mem = AllocateMemory(Size, Flags)
    If *mem
      MemID(Hex(*mem)) = *mem
    Else
      DebuggerWarning("AllocateMemory: Out Of Memory : Proc /" + Proc)
      CompilerIf #MemoryStop : CallDebugger : CompilerEndIf
      ProcedureReturn #False
    EndIf
    ProcedureReturn *mem
  EndProcedure
  
  Procedure MyFreeMemory(Memory, Proc.s)
    If FindMapElement(MemID(), Hex(Memory))
      FreeMemory(Memory)
      DeleteMapElement(MemID())
      ProcedureReturn #True
    Else
      DebuggerWarning("FreeMemory: Memory not exists : Proc /" + Proc)
      CompilerIf #MemoryStop : CallDebugger : CompilerEndIf
      ProcedureReturn #False
    EndIf
  EndProcedure
  
  Procedure MyMemorySize(Memory, Proc.s)
    If FindMapElement(MemID(), Hex(Memory))
      ProcedureReturn MemorySize(Memory)
    Else
      DebuggerWarning("MemorySize: Memory not exists : Proc /" + Proc)
      CompilerIf #MemoryStop : CallDebugger : CompilerEndIf
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  Macro AllocateMemory(Size, Flags=0)
    MyAllocateMemory(Size, Flags, #PB_Compiler_Module + "/" + #PB_Compiler_Procedure + "() - Line " + #PB_Compiler_Line)
  EndMacro
  
  Macro FreeMemory(Memory)
    MyFreeMemory(Memory, #PB_Compiler_Module + "/" + #PB_Compiler_Procedure + "() - Line " + #PB_Compiler_Line)
  EndMacro
  
  Macro MemorySize(Memory)
    MyMemorySize(Memory, #PB_Compiler_Module + "/" + #PB_Compiler_Procedure + "() - Line " + #PB_Compiler_Line)
  EndMacro
  
CompilerEndIf

;- test

Procedure Main()
  *mem1 = AllocateMemory(1024)
  ;*mem2 = AllocateMemory(2048)
  
  Debug "Size 1: " + MemorySize(*mem1)
  Debug "Size 2: " + MemorySize(*mem2)
  
  Debug "Free 1: " + FreeMemory(*mem1)
  Debug "Free 2: " + FreeMemory(*mem2)
EndProcedure : main()
Last edited by mk-soft on Sun Feb 11, 2018 9:07 pm, edited 1 time in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply