12
$\begingroup$

According to the documentation, there are four different type of memory managing when passing a MTensor with LibraryLink: Automatic, "Constant", "Manual" and "Shared".

The Automatic and "Manual" is easy to understand, since in both of them, the MTensor get copied and passed into the library function. The difference between them is wether the tensor will be automatically clean or not. The "Constant" and "Shared" will pass a reference of the MTensor into the library function, and "Constant" promises the library function to not change the MTensor.

I find "Shared" is difficult for me to understand. Does the "Shared" passing means that variable in Mathematica and my library function point to the same data buffer in the memory? And if I change on one side, the changes will be transparent on the other side ?

Consider this example from the document:

loadFun = 
  LibraryFunctionLoad["demo_shared", 
   "loadArray", {{Real, _, "Shared"}}, Integer];
unloadFun = 
  LibraryFunctionLoad["demo_shared", "unloadArray", {}, Integer];
getFunVector = 
  LibraryFunctionLoad["demo_shared", "getElementVector", {Integer}, 
   Real];

If we load the library function with an array

array = Range[1., 20];
loadFun[ array]

and change the array on the Mathematica side

array[[1]] = 2.;
getFunVector[ 1]
(* 1. *)

why the data on the library side not changed?

Also, in the documentation, it says after call unload, which disowns the MTensor, array cannot be used anymore,

unloadFun[ ]
(* 0 *)

getFunVector[1]
(* 1. *)

array[[1]]
(* 2. *)

but why I can still use it? What does disown do?

$\endgroup$
2
  • $\begingroup$ "and "Constant" prevent the library function from changing the MTensor." <-- it doesn't actually prevent modifying the tensor by the library. Using constant passing means that you promise Mathematica not to modify it, but it's up to you to keep your word. If you do modify it anyway, who knows what will happen? Maybe Mathematica will just get very confused. $\endgroup$
    – Szabolcs
    Feb 25, 2014 at 0:40
  • $\begingroup$ @Szabolcs yes you are right, I just tried to replace "Shared" with "Constant" in halirutan's example, and I can call setFunVector to change the "Constant" MTensor without problem. $\endgroup$ Feb 25, 2014 at 0:55

1 Answer 1

12
+50
$\begingroup$

I'm not sure whether I will get everything right here, but to my knowledge the key-point is indeed MTensor_disown. When you call loadFun you basically move the write-priviliges for the array to your library functions. This means, changing values inside the library will be transparent on the Mathematica side. Let's load the library functions:

loadFun = LibraryFunctionLoad["demo_shared", "loadArray", {{Real, 1, "Shared"}}, Integer];
unloadFun = LibraryFunctionLoad["demo_shared", "unloadArray", {}, Integer];
setFunVector = LibraryFunctionLoad["demo_shared", "setElementVector",
    {Integer, Real}, Integer];
getArray = LibraryFunctionLoad["demo_shared", "getArray", {}, {Real, 1}];

Look at this example which loads an array, sets up a dynamic watcher and changes a value from the library side. Note that we watch both, the array on Mathematica side and the MTensor in the library!

array = Range[1., 20];
loadFun[array];   
Dynamic[Refresh[Column@{array, getArray[]}, UpdateInterval -> .5]]

If you now evaluate a setFunVector[4, -1] you see that both lists are updated because they represent the same tensor.

However, what you have to keep in mind is that as long as you have not disowned the tensor in the library, you have not right to change the values from Mathematica side. If you do it anyway, then I guess Mathematica copies the current tensor and lets you modify this instead. Once you have done this, you have two tensors and changing the value from inside the library will only change your original one:

array[[1]] = 0
(*
  {0,2.,3.,-1.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.,17.,18.,19.,20.}
  {1.,2.,3.,-1.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.,17.,18.,19.,20.}
*)

And now see that only the library tensor is changed

setFunVector[4, 10]
(*
  {0,2.,3.,-1.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.,17.,18.,19.,20.}
  {1.,2.,3.,10.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.,17.,18.,19.,20.}
*)

The conclusion is that you cannot have parallel access from both sides. If you want to make a change from within Mathematica you have to disown the tensor in the library, change it inside Mathematica and re-load it. This is my understanding of the situation.

Edit

I forgot one important thing which was pointed out by rasher in the comments: Every time you pass a tensor to the library in shared mode, Mathematica makes an entry in a shared tensor table. Therefore, if you pass it several times without disowning it, you will end up having several entries in this table and one call to MTensor_disown is not enough. You have to call it as often as you have passed the tensor to the library. For this the function MTensor_disownAll is convenient.

Furthermore, while we are at the topic of passing shared tensors from Mathematica to a library function: There is additionally the possibility to return shared tensors. You can read about this in the official documentation.

$\endgroup$
5
  • $\begingroup$ Another, shorter way to put it: Only one side may write to the tensor. "Shared" passing transfers write rights to the library. Disowning transfers them back to Mathematica. The documentation implies that this might trigger Mathematica freeing up the tensor. What's not clear to me is whether it's ever okay not to call disown before the library function returns. $\endgroup$
    – Szabolcs
    Feb 25, 2014 at 0:43
  • $\begingroup$ That's very illustrative, big thanks. @Szabolcs that's exactly what I was going to ask. $\endgroup$ Feb 25, 2014 at 0:50
  • 1
    $\begingroup$ @Szabolcs I think it is OK to make several calls to the library before you disown the tensor. You just have to remember not to touch the tensor from Mathematica side. Since the work with LinkLibrary can be considered dangerous in general, because you can easily break something, this should be a minor addition you have to care about. If it wouldn't be OK to keep the rights on a tensor, why would disown exist at all and not be called automatically at the end of every function? $\endgroup$
    – halirutan
    Feb 25, 2014 at 1:00
  • 2
    $\begingroup$ +1, nice answer. And readers should note (IIRC) that failing to use disown on each invocation, you need to call it multiple times (for as many shared references that were made), count in MTensor_shareCount, or use disownAll... $\endgroup$
    – ciao
    Feb 25, 2014 at 1:20
  • $\begingroup$ @rasher I included your comment in the answer. Thanks. $\endgroup$
    – halirutan
    Feb 25, 2014 at 1:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.