Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/c-api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.18)
project(c_api)


add_subdirectory(src)
11 changes: 11 additions & 0 deletions examples/c-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Using cereggii's C-APIs

This examples showcases how to use cereggii's C-level APIs, highlighting how to
correctly specify it as a build-time dependency.

The problems:

1. we need to be able to compile the program using cereggii's headers.
2. we may additionally need to link the pre-built shared library to use the C-APIs
at runtime.
- you don't need to link against the shared library, if you only need the header-only atomics.
34 changes: 34 additions & 0 deletions examples/c-api/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[build-system]
requires = [
"cereggii",
"py-build-cmake",
]
build-backend = "py_build_cmake.build"

[project]
name = "c_api"
readme = "README.md"
requires-python = ">=3.13"
authors = [
{ name = "dpdani", email = "git@danieleparmeggiani.me" },
]
dependencies = [
"cereggii"
]

[tool.py-build-cmake.module]
directory = "src"

[tool.py-build-cmake.sdist]
include = [
"CMakeLists.txt",
"src/CMakeLists.txt",
]

[tool.py-build-cmake.cmake]
minimum_version = "3.18"
build_type = "Debug"
source_path = "src"
args = ["-L"]
build_args = ["-j"]
install_components = ["python_modules"]
41 changes: 41 additions & 0 deletions examples/c-api/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.18)
project(c_api)

set(PY_BUILD_CMAKE_MODULE_NAME "c_api")

# find Python.h et al.
execute_process(COMMAND python -c "import sysconfig; print(sysconfig.get_path('include'), end='')" OUTPUT_VARIABLE Python3_INCLUDE_DIR)
find_package(Python3 REQUIRED COMPONENTS Development.Module)

# find cereggii/*.h
execute_process(COMMAND python -c "import cereggii; from pathlib import Path; print(Path(cereggii.__file__).parent, end='')" OUTPUT_VARIABLE CEREGGII_INSTALLATION_DIR)
message("CEREGGII_INSTALLATION_DIR=${CEREGGII_INSTALLATION_DIR}")

execute_process(COMMAND python -c "import cereggii; print(cereggii.__version__, end='')" OUTPUT_VARIABLE CEREGGII_VERSION)
message(${CEREGGII_VERSION})

set(CEREGGII_INCLUDE_DIR "${CEREGGII_INSTALLATION_DIR}/include")
if (NOT IS_DIRECTORY ${CEREGGII_INCLUDE_DIR})
message(FATAL_ERROR "Module cereggii not found in ${CEREGGII_INCLUDE_DIR}. Please add it to the build-system.requires section of your pyproject.toml file.")
endif ()
set(CEREGGII_LIB "${CEREGGII_INSTALLATION_DIR}/libcereggii.${CEREGGII_VERSION}.dylib")
if (NOT EXISTS ${CEREGGII_LIB})
message(FATAL_ERROR "cereggii shared object not found in ${CEREGGII_LIB}.")
endif ()

# add cereggii to the search path for #include
include_directories(${CEREGGII_INCLUDE_DIR})

link_libraries(${CEREGGII_LIB})


# Add the module to compile
Python3_add_library(_c_api MODULE
"c_api/c_api.c"
)

# Install the module
install(TARGETS _c_api
EXCLUDE_FROM_ALL
COMPONENT python_modules
DESTINATION ${PY_BUILD_CMAKE_MODULE_NAME})
10 changes: 10 additions & 0 deletions examples/c-api/src/c_api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import sys
from pathlib import Path

import cereggii

sys.path.append(str(Path(cereggii.__file__).parent))

from c_api._c_api import *

__version__ = "1.0"
92 changes: 92 additions & 0 deletions examples/c-api/src/c_api/c_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "cereggii/atomics.h"

typedef struct {
PyObject_HEAD
Py_ssize_t ref;
} PyAtomic;

// Constructor: Atomic(ref)
static int PyAtomic_init(PyAtomic *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t initial;
if (!PyArg_ParseTuple(args, "n", &initial)) {
return -1;
}
self->ref = initial;
return 0;
}

// Destructor
static void PyAtomic_dealloc(PyAtomic* self) {
Py_TYPE(self)->tp_free((PyObject*)self);
}

// Atomic compare-and-exchange: Atomic.compare_exchange(expected, desired)
static PyObject* PyAtomic_compare_exchange(PyAtomic *self, PyObject *args) {
Py_ssize_t expected;
Py_ssize_t desired;

if (!PyArg_ParseTuple(args, "nn", &expected, &desired)) {
return NULL;
}

int success = CereggiiAtomic_CompareExchangeSsize(&self->ref, expected, desired);
return PyLong_FromLong(success);
}

static PyObject* PyAtomic_get(PyAtomic *self, PyObject *args) {
return PyLong_FromSsize_t(self->ref);
}

// Atomic methods
static PyMethodDef PyAtomic_methods[] = {
{"compare_exchange", (PyCFunction)PyAtomic_compare_exchange, METH_VARARGS, ""},
{"get", (PyCFunction)PyAtomic_get, METH_NOARGS, ""},
{NULL} // Sentinel
};

// Atomic type definition
static PyTypeObject PyAtomicType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "atomic_module.Atomic",
.tp_basicsize = sizeof(PyAtomic),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
.tp_init = (initproc) PyAtomic_init,
.tp_dealloc = (destructor) PyAtomic_dealloc,
.tp_methods = PyAtomic_methods,
};

// Module methods
static PyMethodDef AtomicMethods[] = {
{NULL, NULL, 0, NULL}
};

// Module definition
static struct PyModuleDef atomicmodule = {
PyModuleDef_HEAD_INIT,
"atomic_module",
"Python wrapper for atomic operations",
-1,
AtomicMethods
};

// Module initialization
PyMODINIT_FUNC PyInit__c_api(void) {
PyObject *m;
if (PyType_Ready(&PyAtomicType) < 0) return NULL;

m = PyModule_Create(&atomicmodule);
if (!m) return NULL;

Py_INCREF(&PyAtomicType);
if (PyModule_AddObject(m, "Atomic", (PyObject *) &PyAtomicType) < 0) {
Py_DECREF(&PyAtomicType);
Py_DECREF(m);
return NULL;
}

return m;
}
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ include = [
"CMakeLists.txt",
"src/CMakeLists.txt",
"src/cereggiiconfig.h.in",
"src/include/*",
"src/cereggii/include/*",
]

[tool.py-build-cmake.cmake]
Expand All @@ -70,6 +70,12 @@ build_args = ["-j"]
install_components = ["python_modules"]


[tool.cibuildwheel]
enable = [
"cpython-freethreading",
]
build = "cp313*-manylinux_aarch64"

[tool.black]
target-version = ["py313"]
line-length = 120
Expand Down
31 changes: 24 additions & 7 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ set(PY_BUILD_CMAKE_MODULE_NAME "cereggii")

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
add_compile_options("-mcx16")
add_compile_options("-msse4.2")
execute_process(COMMAND getconf LEVEL1_DCACHE_LINESIZE OUTPUT_VARIABLE LEVEL1_DCACHE_LINESIZE)
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
Expand All @@ -15,16 +14,15 @@ else ()
message(FATAL_ERROR "Unsupported platform: ${CMAKE_HOST_SYSTEM_NAME}")
endif ()

configure_file(cereggiiconfig.h.in "${CMAKE_CURRENT_SOURCE_DIR}/include/internal/cereggiiconfig.h")
configure_file(cereggiiconfig.h.in "${CMAKE_CURRENT_SOURCE_DIR}/include/cereggii/internal/cereggiiconfig.h")

execute_process(COMMAND python -c "import sysconfig; print(sysconfig.get_path('include'), end='')" OUTPUT_VARIABLE Python3_INCLUDE_DIR)
find_package(Python3 REQUIRED COMPONENTS Development.Module)
find_package(Python3 REQUIRED COMPONENTS Development)


include_directories("include/")
include_directories("cereggii/include/" PUBLIC)

# Add the module to compile
Python3_add_library(_cereggii MODULE
set(sources
"cereggii/atomic_dict/accessor_storage.c"
"cereggii/atomic_dict/atomic_dict.c"
"cereggii/atomic_dict/blocks.c"
Expand All @@ -38,18 +36,37 @@ Python3_add_library(_cereggii MODULE
"cereggii/atomic_int/atomic_int.c"
"cereggii/atomic_int/handle.c"
"cereggii/atomic_event.c"
"cereggii/atomic_ops.c"
"cereggii/atomic_ref.c"
"cereggii/cereggii.c"
"cereggii/constants.c"
)

# Add the module to compile
Python3_add_library(_cereggii MODULE ${sources})

# Add library... todo
set(CEREGGII_LIB "cereggii.${PY_BUILD_CMAKE_PROJECT_VERSION}")
Python3_add_library(${CEREGGII_LIB} SHARED ${sources})

# Set visibility rules
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 0)

# Define export macro
target_compile_definitions(${CEREGGII_LIB} PRIVATE BUILDING_CEREGGII)

# Install the module
install(TARGETS _cereggii
EXCLUDE_FROM_ALL
COMPONENT python_modules
DESTINATION ${PY_BUILD_CMAKE_MODULE_NAME})

# Install the library
install(TARGETS ${CEREGGII_LIB}
EXCLUDE_FROM_ALL
COMPONENT python_modules
DESTINATION ${PY_BUILD_CMAKE_MODULE_NAME})

# Install stubs to get autocomplete and type hints
install(FILES cereggii/_cereggii.pyi
EXCLUDE_FROM_ALL
Expand Down
4 changes: 2 additions & 2 deletions src/cereggii/atomic_dict/accessor_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

#include "atomic_dict.h"
#include "atomic_dict_internal.h"
#include "cereggii/atomic_dict.h"
#include "cereggii/atomic_dict_internal.h"


AtomicDict_AccessorStorage *
Expand Down
6 changes: 3 additions & 3 deletions src/cereggii/atomic_dict/atomic_dict.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

#define PY_SSIZE_T_CLEAN

#include "atomic_dict.h"
#include "atomic_dict_internal.h"
#include "atomic_ref.h"
#include "cereggii/atomic_dict.h"
#include "cereggii/atomic_dict_internal.h"
#include "cereggii/atomic_ref.h"
#include "pythread.h"


Expand Down
6 changes: 3 additions & 3 deletions src/cereggii/atomic_dict/blocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

#define PY_SSIZE_T_CLEAN

#include "atomic_dict.h"
#include "atomic_dict_internal.h"
#include "atomic_ops.h"
#include "cereggii/atomic_dict.h"
#include "cereggii/atomic_dict_internal.h"
#include "cereggii/atomics.h"


AtomicDict_Block *
Expand Down
4 changes: 2 additions & 2 deletions src/cereggii/atomic_dict/delete.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

#include "atomic_dict_internal.h"
#include "atomic_ops.h"
#include "cereggii/atomic_dict_internal.h"
#include "cereggii/atomics.h"


int
Expand Down
10 changes: 5 additions & 5 deletions src/cereggii/atomic_dict/insert.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

#define PY_SSIZE_T_CLEAN

#include "constants.h"
#include "atomic_dict_internal.h"
#include "atomic_ref.h"
#include "atomic_ops.h"
#include "cereggii/constants.h"
#include "cereggii/atomic_dict_internal.h"
#include "cereggii/atomic_ref.h"
#include "cereggii/atomics.h"
#include "pythread.h"
#include "_internal_py_core.h"
#include "cereggii/_internal_py_core.h"


int
Expand Down
4 changes: 2 additions & 2 deletions src/cereggii/atomic_dict/iter.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#define PY_SSIZE_T_CLEAN

#include "atomic_dict.h"
#include "atomic_dict_internal.h"
#include "cereggii/atomic_dict.h"
#include "cereggii/atomic_dict_internal.h"


PyObject *
Expand Down
6 changes: 3 additions & 3 deletions src/cereggii/atomic_dict/lookup.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

#define PY_SSIZE_T_CLEAN

#include "atomic_dict.h"
#include "atomic_dict_internal.h"
#include "constants.h"
#include "cereggii/atomic_dict.h"
#include "cereggii/atomic_dict_internal.h"
#include "cereggii/constants.h"


void
Expand Down
4 changes: 2 additions & 2 deletions src/cereggii/atomic_dict/meta.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#define PY_SSIZE_T_CLEAN

#include "atomic_dict_internal.h"
#include "atomic_ref.h"
#include "cereggii/atomic_dict_internal.h"
#include "cereggii/atomic_ref.h"
#include "pythread.h"


Expand Down
Loading