Forum Discussion

FICO's avatar
FICO
Community Manager
9 months ago

Are preintsol callbacks threadsafe?

The optimizer documentation states "When the MIP callbacks are called they are MUTEX protected to allow non threadsafe user callbacks." In my Java code for one-tree Benders, if I specify a single thread for the master problem via master.controls().setThreads(1), the problem solves. If I remove that line, allowing multiple threads, I get a series of 1056 errors:

?1056 Error: Object passed as parameter 'prob' of function XPRSgetintattrib is currently being used by the optimizer - you should only use this object when the solver is not using this object or within a callback

Eventually, the solver crashes (apparently while attempting to print a cut, according to the stack trace). This all takes place in a preintsol callback. The function preIntsol is declared as synchronized, which apparently does not help.

Can I use a preintsol callback with multiple threads and, if so, how?

Thanks.

3 Replies

  • FICO's avatar
    FICO
    Community Manager

    Callbacks are in general thread safe. The error message suggests that you are using the wrong problem reference when interacting with Xpress during a callback.

    When interacting with Xpress during a callback, you should use the problem reference that is passed into the callback. The error message you are seeing suggests that you are instead using the global problem instance, i.e., the one on which you called optimize.

    The problem reference passed into a callback is a thread-local problem reference. As long as you only use that reference in the callback, your callback is thread-safe with respect to Xpress.

    Your single-threaded code works because with only one thread, the problem reference passed into the callback is the global problem.

  • FICO's avatar
    FICO
    Community Manager

    I've narrowed the bug down to a print statement. When I create my master MIP (instance of XpressProblem), I create 15 binary variables (stored in Variable[] use) and one continuous variable (stored in Variable flowCost). Inside the callback, I create an instance of LinExpression using the use variables, then add a cut based on that expression and the flowCost. That seems to be fine, even with multiple threads. The exception occurs (sometimes, not always) when I try to print the cut and/or the value of flowCost.

    Just now I tried to replace the original variables with those associated with the prob argument. Since I could not find a direct way to reference them, I tried prob.variableForIndex(i) as a replacement for use. That earned me a null pointer exception because "this.variableHandler" is null.

    I fixed the problem by adapting your code (in your answer to another of my questions) for accessing the original names of the cut variables. This seems to work with multiple threads. Thanks for that code! It does seem to me to be unnecessarily clunky in this context. If a LinExpression instance inside a callback can use the original variables and produce correct cuts (throwing no exceptions), it seems that the toString method of that LinExpression should also work.

  • FICO's avatar
    FICO
    Community Manager

    I agree that the toString() function should work. Unfortunately, at the moment it does not and at the moment this is the expected behavior (for the technical reasons described above). I have filed an internal request for improvement. Obviously, printing out a cut for debugging reasons is a very valid use case that should work out of the box.

    You get this NullPointerException from XpressProblem.getVariables() because the problem passed into the callback is in presolved state and thus has no Variable instances: interaction with a model in presolved state requires indices rather than Variable instances to reference columns.

    So everything boils down to the known limitation that LinExpression.toString() does not work from inside a callback.