Raku NativeCalls provide a way to interact with dynamic libraries that follow the C calling convention and are very useful for obtaining information from the operating system, such as memory usage.
In this article we will see how to get the memory usage from a Windows system.
Win32 API provides the MEMORYSTATUSEX
structure:
typedef struct _MEMORYSTATUSEX {
DWORD dwLength;
DWORD dwMemoryLoad;
DWORDLONG ullTotalPhys;
DWORDLONG ullAvailPhys;
DWORDLONG ullTotalPageFile;
DWORDLONG ullAvailPageFile;
DWORDLONG ullTotalVirtual;
DWORDLONG ullAvailVirtual;
DWORDLONG ullAvailExtendedVirtual;
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;
that contains information about the current memory in bytes, including:
- Total physical memory:
ullTotalPhys
- Available physical memory:
ullAvailPhys
These two members will allow us to calculate the memory usage by substracting ullAvailPhys
from ullTotalPhys
.
The MEMORYSTATUSEX
structure has the dwLength
member that needs to be set before calling the function GlobalMemoryStatusEx
that populates the MEMORYSTATUSEX
structure.
Raku Nativecalls use the repr('CStruct')
trait into a declaration class to interact with C structures. We can declare this class to use the C++ MEMORYSTATUSEX
structure as follows:
class MEMORYSTATUSEX is repr('CStruct') {
has uint32 $.dwLength is rw;
has uint32 $.dwMemoryLoad;
has uint64 $.ullTotalPhys;
has uint64 $.ullAvailPhys;
has uint64 $.ullTotalPageFile;
has uint64 $.ullAvailPageFile;
has uint64 $.ullTotalVirtual;
has uint64 $.ullAvailVirtual;
has uint64 $.ullAvailExtendedVirtual;
};
It's important mapping the member types between C++ and Raku:
- The Raku type
uint32
is equivalent to C++ Win32DWORD
type. - The Raku type
uint64
is equivalent to C++ Win32DWORDLONG
type.
The $.dwLength
member is rw (read and write) to set the size of the structure later.
This C++ function populates the MEMORYSTATUSEX
structure using as argument the LPMEMORYSTATUSEX
pointer type:
BOOL GlobalMemoryStatusEx( LPMEMORYSTATUSEX lpBuffer );
Raku doesn't use pointers to perform a Native Call to this function, instead it uses a Raku function with the native
trait as we will see below.
Raku Native Calls allows to use the C++ GlobalMemoryStatusEx
function as follows:
use NativeCall;
sub GlobalMemoryStatusEx(MEMORYSTATUSEX) is native('Kernel32') returns int32 { * };
use NativeCall
loads the Raku NativeCall context.GlobalMemoryStatusEx
is the function name and must be the same as the original name in C++.MEMORYSTATUSEX
is the name of the class we have declared before to interact with the C++ struct that will contain the members we need ($.ullTotalPhys
and$.ullAvailPhys
). Actually, this class acts as the pointer that goes into the argument of the C++GlobalMemoryStatusEx
function.- The trait
is native('Kernel32')
exposes the Win32 library that contains theGlobalMemoryStatusEx
function. - This function returns a bool value that is represented by the
int32
type.
To use the MEMORYSTATUSEX
class with the GlobalMemoryStatusEx
Native function we need to instanciate it in an object, $data
for example:
my MEMORYSTATUSEX $data .=new;
Also, let's not forget to pass the size of the structure (or $data object) setting the $data.dwLength
member with the current size. The structure size is the $data
object size and we can get it with the function nativesizeof
:
$data.dwLength = nativesizeof($data);
Now we are ready to populate the MEMORYSTATUSEX
struct ($data object) calling the GlobalMemoryStatusEx
Native function with the $data
object as argument:
GlobalMemoryStatusEx($data);
The $data
object acts like the C++ LPMEMORYSTATUSEX
pointer.
Finnaly, the results that we need are in the values of the members of the $data
object:
my $memoryUsage = $data.ullTotalPhys - $data.ullAvailPhys;
say "Current Memory Usage: $memoryUsage Bytes.";
Here you have available this example applied in a Raku module.
As we have seen in this example, the use of Raku Native Calls allows extending the Raku's functionality to the universe of the operating system through its dynamic libraries. Furthermore, by making the appropriate calls to different operating systems, we can create applications that work on any of them.
More information about Raku Native calling interface in the Raku documentation.