/*
    Copyright (c) 2022 Google LLC

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE file.
*/

#include <pybind11/pybind11.h>

// This file mimics a DSO that makes pybind11 calls but does not define a PYBIND11_MODULE,
// so that the first call of cross_module_error_already_set() triggers the first call of
// pybind11::detail::get_internals().

namespace {

namespace py = pybind11;

void interleaved_error_already_set() {
    PyErr_SetString(PyExc_RuntimeError, "1st error.");
    try {
        throw py::error_already_set();
    } catch (const py::error_already_set &) {
        // The 2nd error could be conditional in a real application.
        PyErr_SetString(PyExc_RuntimeError, "2nd error.");
    } // Here the 1st error is destroyed before the 2nd error is fetched.
    // The error_already_set dtor triggers a pybind11::detail::get_internals()
    // call via pybind11::gil_scoped_acquire.
    if (PyErr_Occurred()) {
        throw py::error_already_set();
    }
}

constexpr char kModuleName[] = "cross_module_interleaved_error_already_set";

struct PyModuleDef moduledef = {
    PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr};

} // namespace

extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_already_set() {
    PyObject *m = PyModule_Create(&moduledef);
    if (m != nullptr) {
        static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *),
                      "Function pointer must have the same size as void *");
        PyModule_AddObject(
            m,
            "funcaddr",
            PyLong_FromVoidPtr(reinterpret_cast<void *>(&interleaved_error_already_set)));
    }
    return m;
}
