patterncsharpMinor
C# to C++ function call that fills and auto resize array if size is not enough
Viewed 0 times
fillsautoresizearraysizefunctioncallthatandnot
Problem
I have a C# function that calls a C++ function to fill the array with pixel data from a camera. This is better than allocating memory on the C++ side and marshaling it on C# side each time the function is called.
I thought about running into a scenario where a camera that outputs 720p can receive a command to output 1080p image. When this happens the array size allocated from C# may not be enough to hold the pixel information. That current function call will fail. I do not want to throw an exception and do not want it to fail. It must continue and should not discard that frame.
My current solution is to check if the size of the pixels to write to the array is
If the size of the pixels to write is bigger than the array, allocate new memory then return it to C#. After that resize the size of the array from C# then use
Bascially, when this problem happens, I will allocate memory on both sides (C++ and C#).
How can this be improved?
Note that I had to use
C++:
```
int test6FillArrayWithAutoResize(int data, int count, int* outValue) {
//For testing Purposes. Used to determine how much size to write to the data variable
const int requiredMemory = 20;
//Create new and Resize if memory is not enough
if (requiredMemory > count) {
int* newVar = new int[requiredMemory];
//Do something to the variable then return it
for (int i = 0; i < requiredMemory; i++)
{
newVar[i] = i;
}
//Tell caller memory size
*outValue = requiredMemory;
//Return the new created variable
I thought about running into a scenario where a camera that outputs 720p can receive a command to output 1080p image. When this happens the array size allocated from C# may not be enough to hold the pixel information. That current function call will fail. I do not want to throw an exception and do not want it to fail. It must continue and should not discard that frame.
My current solution is to check if the size of the pixels to write to the array is
< the size of the array allocated from C# before writing to that array from C++ side. If the size of the pixels to write is bigger than the array, allocate new memory then return it to C#. After that resize the size of the array from C# then use
Marshal.Copy to copy the data returned from C++ to that newly resized array in C#. Finally call a function from C++ side to free the memory allocated there.Bascially, when this problem happens, I will allocate memory on both sides (C++ and C#).
How can this be improved?
Note that I had to use
unsafe and fixed keywords to pin the array for maximum performance after days of testing this with other methods. Anything faster than this is also acceptable.C++:
```
int test6FillArrayWithAutoResize(int data, int count, int* outValue) {
//For testing Purposes. Used to determine how much size to write to the data variable
const int requiredMemory = 20;
//Create new and Resize if memory is not enough
if (requiredMemory > count) {
int* newVar = new int[requiredMemory];
//Do something to the variable then return it
for (int i = 0; i < requiredMemory; i++)
{
newVar[i] = i;
}
//Tell caller memory size
*outValue = requiredMemory;
//Return the new created variable
Solution
Your situation immediately reminded me of the "PC loser-ing" problem: a classic case study contrasting the design philosophies of MIT's ITS operating system and the UNIX operating system developed at Bell Labs.
Two famous people, one from MIT and another from Berkeley (but working
on Unix) once met to discuss operating system issues. The person from
MIT was knowledgeable about ITS (the MIT AI Lab operating system) and
had been reading the Unix sources. He was interested in how Unix
solved the PC loser-ing problem. The PC loser-ing problem occurs when
a user program invokes a system routine to perform a lengthy operation
that might have significant state, such as IO buffers. If an interrupt
occurs during the operation, the state of the user program must be
saved. Because the invocation of the system routine is usually a
single instruction, the PC of the user program does not adequately
capture the state of the process. The system routine must either back
out or press forward. The right thing is to back out and restore the
user program PC to the instruction that invoked the system routine so
that resumption of the user program after the interrupt, for example,
re-enters the system routine. It is called "PC loser-ing" because
the PC is being coerced into "loser mode," where "loser" is the
affectionate name for "user" at MIT.
The MIT guy did not see any code that handled this case and asked the
New Jersey guy how the problem was handled. The New Jersey guy said
that the Unix folks were aware of the problem, but the solution was
for the system routine to always finish, but sometimes an error code
would be returned that signaled that the system routine had failed to
complete its action. A correct user program, then, had to check the
error code to determine whether to simply try the system routine
again. The MIT guy did not like this solution because it was not the
right thing.
The New Jersey guy said that the Unix solution was right because the
design philosophy of Unix was simplicity and that the right thing was
too complex. Besides, programmers could easily insert this extra test
and loop. The MIT guy pointed out that the implementation was simple
but the interface to the functionality was complex. The New Jersey guy
said that the right tradeoff has been selected in Unix-namely,
implementation simplicity was more important than interface
simplicity.
Based on that lesson, I would seriously reconsider your design goal:
I do not want to throw an exception and do not want it to fail. It must continue and should not discard that frame.
Consider the consequences of that goal:
I would also challenge your fear of discarding a frame. You would have the same problem even if you were coding purely in C++.
Here's a design that does all of the buffer allocation in C#. The C++ function returns the size of the required buffer. If your camera's API allows you to query the required frame size before retrieving the frame data, then the code could be even simpler than the solution below.
C++:
{
int offset = 0, frameSize;
while (true)
{
fixed (int* p = array) // Pin memory
{
frameSize = offset + testFillArray((IntPtr)p, offset, array.Length - offset);
}
if (frameSize
Nitpicks
In your C++ code, in your
Use braces consistently. In your C++ code, you used a different brace style for your
Two famous people, one from MIT and another from Berkeley (but working
on Unix) once met to discuss operating system issues. The person from
MIT was knowledgeable about ITS (the MIT AI Lab operating system) and
had been reading the Unix sources. He was interested in how Unix
solved the PC loser-ing problem. The PC loser-ing problem occurs when
a user program invokes a system routine to perform a lengthy operation
that might have significant state, such as IO buffers. If an interrupt
occurs during the operation, the state of the user program must be
saved. Because the invocation of the system routine is usually a
single instruction, the PC of the user program does not adequately
capture the state of the process. The system routine must either back
out or press forward. The right thing is to back out and restore the
user program PC to the instruction that invoked the system routine so
that resumption of the user program after the interrupt, for example,
re-enters the system routine. It is called "PC loser-ing" because
the PC is being coerced into "loser mode," where "loser" is the
affectionate name for "user" at MIT.
The MIT guy did not see any code that handled this case and asked the
New Jersey guy how the problem was handled. The New Jersey guy said
that the Unix folks were aware of the problem, but the solution was
for the system routine to always finish, but sometimes an error code
would be returned that signaled that the system routine had failed to
complete its action. A correct user program, then, had to check the
error code to determine whether to simply try the system routine
again. The MIT guy did not like this solution because it was not the
right thing.
The New Jersey guy said that the Unix solution was right because the
design philosophy of Unix was simplicity and that the right thing was
too complex. Besides, programmers could easily insert this extra test
and loop. The MIT guy pointed out that the implementation was simple
but the interface to the functionality was complex. The New Jersey guy
said that the right tradeoff has been selected in Unix-namely,
implementation simplicity was more important than interface
simplicity.
Based on that lesson, I would seriously reconsider your design goal:
I do not want to throw an exception and do not want it to fail. It must continue and should not discard that frame.
Consider the consequences of that goal:
- You do the memory allocation in two ways: usually on the C# side, but sometimes on the C++ side. That's two codepaths to maintain and test.
- If the C++ allocation mechanism worked well (reliably, maintainably, and fast), why wouldn't you use it as the sole solution? As you stated, having C# be responsible for array allocation is simply better, except when there is insufficient capacity.
I would also challenge your fear of discarding a frame. You would have the same problem even if you were coding purely in C++.
Here's a design that does all of the buffer allocation in C#. The C++ function returns the size of the required buffer. If your camera's API allows you to query the required frame size before retrieving the frame data, then the code could be even simpler than the solution below.
C++:
int testFillArray(int* data, int offset, int count) {
//For testing Purposes. Used to determine how much size to write to the data variable
const int requiredMemory = 20;
if (requiredMemory
C#:
public unsafe int getTestFillArray(ref int[] array){
int offset = 0, frameSize;
while (true)
{
fixed (int* p = array) // Pin memory
{
frameSize = offset + testFillArray((IntPtr)p, offset, array.Length - offset);
}
if (frameSize
Nitpicks
In your C++ code, in your
//Memory is enough case, what happens if the camera produces less data than the array size? You would write junk data at the end of the array.Use braces consistently. In your C++ code, you used a different brace style for your
if than for your loops. You should also not omit "optional" braces, and if you must omit them, write the condition and statement on the same line.Context
StackExchange Code Review Q#162088, answer score: 5
Revisions (0)
No revisions yet.