-
Notifications
You must be signed in to change notification settings - Fork 1
Available license attempt 2 #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
|
||
|
|
||
| class _LicenseManager: | ||
| def acquire(self, timeout: Optional[float] = None) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put in a docstring (from the issue in which I took notes) explaining how users are expected to interact with licenses.
| return | ||
|
|
||
| if not timeout: | ||
| cls._set_env(gurobipy.Env()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each Pyomo thread would need its own gurobi.Env - because a single thread needs a single Env.
| @classmethod | ||
| def _register_env_client(cls): | ||
| cls._num_gurobipy_env_clients += 1 | ||
|
|
||
| @classmethod | ||
| def _release_env_client(cls): | ||
| if cls._num_gurobipy_env_clients > 0: | ||
| cls._num_gurobipy_env_clients -= 1 | ||
| env = cls._get_env() | ||
| if cls._num_gurobipy_env_clients <= 0 and env is not None: | ||
| if cls._num_gurobipy_env_clients < 0: | ||
| logger.warning( | ||
| "Gurobi env client refcount went negative " | ||
| f"({cls._num_gurobipy_env_clients}). " | ||
| "This should not have happened and should be reported to " | ||
| "Pyomo development team." | ||
| ) | ||
| try: | ||
| env.close() | ||
| except Exception as err: | ||
| logger.warning(f"Exception while closing Gurobi environment: {err!r}") | ||
| finally: | ||
| cls._set_env(None) | ||
|
|
||
| @classmethod | ||
| def _get_env(cls): | ||
| """Return the current shared Env (or None)""" | ||
| if cls._gurobipy_env_key is None: | ||
| return None | ||
| return _GUROBI_ENV_REGISTRY.get(cls._gurobipy_env_key) | ||
|
|
||
| @classmethod | ||
| def _set_env(cls, env): | ||
| """Install or clear the current process-local Env""" | ||
| if env is None: | ||
| if cls._gurobipy_env_key is not None: | ||
| _GUROBI_ENV_REGISTRY.pop(cls._gurobipy_env_key, None) | ||
| cls._gurobipy_env_key = None | ||
| else: | ||
| if cls._gurobipy_env_key is None: | ||
| cls._gurobipy_env_key = id(cls) | ||
| _GUROBI_ENV_REGISTRY[cls._gurobipy_env_key] = env | ||
|
|
||
| @classmethod | ||
| def _ensure_env(cls): | ||
| """ | ||
| Return a live gurobipy.Env. If current env exists but is closed, | ||
| recreate it. | ||
| """ | ||
| env = cls._get_env() | ||
| if env is None: | ||
| return None | ||
| if not hasattr(env, "_cenv"): | ||
| try: | ||
| env.close() | ||
| except Exception: | ||
| pass | ||
| env = gurobipy.Env() | ||
| cls._set_env(env) | ||
| return env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should all move to the _GurobiLicenseManager
| return _cm() | ||
|
|
||
|
|
||
| class GurobiSolverMixin: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double-check that the default behavior is that only one Env is created even if you make both a gurobi_direct and gurobi_persistent solver at the same time.
| # requirement that the direct interface doesn't) | ||
| if not self._is_gp_available(): | ||
| self.__class__._available_cache = Availability.NotFound | ||
| else: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use the license manager instead. (Which I half-did - whoops, gotta fix this. Def a bug.)
| vtype=vtype, | ||
|
|
||
| # Acquire a Gurobi env for the duration of solve (opt + postsolve): | ||
| with self.license(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense to do this in solve, but let's make sure that acquiring a license twice doesn't do anything (e.g., if a user runs solver.license.acquire(), it won't try to acquire another license).
|
|
||
| class _LicenseManager: | ||
| def acquire(self, timeout: Optional[float] = None) -> None: | ||
| """Acquire and lock a license. Default behavior is to simply return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had a discussion about timeout here - there is a difference between "time allowing the server to try before we give up / cancel" vs. "we are retrying for X amount of time, only if we keep getting errors"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeout (flaky network / talking to license server) and retry (programming convenience, keep trying until a license becomes available) or whatever. If one/both are irrelevant, great, ignore it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeout- infinityretry- left at solver default (None/ not set in some way)
Fixes # .
Summary/Motivation:
Changes proposed in this PR:
Legal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: