Skip to content
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

Add shared library option #236

Merged
merged 16 commits into from
Jan 18, 2024
Merged

Conversation

uilianries
Copy link
Contributor

This PR includes the new option LIBCORO_BUILD_SHARED_LIBS that reflects the CMake option for BUILD_SHARED_LIBS. Once enabled, it will build only shared libraries and not static.

When using the vendor c-ares, it will keep both aligned, by using shared/shared or static/static.

The O.S. Windows requires __declspec to export symbols when providing a .dll. However, the libcoro project exposes all headers as public, so I just took a shortcut by using CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS instead. Using dllimport/dllexport in this case, would result in the same exported symbols, but only creating more logic to disable/enable in headers.

closes #233

@uilianries uilianries marked this pull request as draft January 10, 2024 12:46
@uilianries
Copy link
Contributor Author

Hello @jbaldwin !

I added not the shared library option, but also new builds in the Github Action to make sure it will work. However, now I see several linkage errors, for instance:

https://github.com/jbaldwin/libcoro/actions/runs/7474611322/job/20341130462?pr=236

FAILED: examples/coro_task 
: && /usr/bin/g++-10  -O3 -DNDEBUG   examples/CMakeFiles/coro_task.dir/coro_task.cpp.o  -o examples/coro_task  -Wl,-rpath,/__w/libcoro/libcoro/Release:/__w/libcoro/libcoro/Release/vendor/c-ares/c-ares/lib  libcoro.so  -lpthread  vendor/c-ares/c-ares/lib/libcares.so.2.4.2  /usr/lib/x86_64-linux-gnu/libssl.so  /usr/lib/x86_64-linux-gnu/libcrypto.so && :
/usr/bin/ld: libcoro.so: undefined reference to `coro::io_scheduler::schedule(coro::task<void>&&)'
/usr/bin/ld: libcoro.so: undefined reference to `coro::io_scheduler::poll(int, coro::poll_op, std::chrono::duration<long, std::ratio<1l, 1000l> >)'

The configuration is clear about not using platform support (LIBCORO_FEATURE_PLATFORM=OFF), but it's actually always required.

The examples/coro_task.cpp does not include io_scheduler.hpp, but only coro/coro.hpp. The main include coro.hpp has a big ifdef to include or not scheduler too. So we know when LIBCORO_FEATURE_PLATFORM=OFF, those headers and source files are not compiler/included.

However, I can see schedule usage both in include/coro/task_container.hpp and include/coro/concepts/executor.hpp. The schedule method is implemented by io_scheduler.cpp.

Am I missing something, or LIBCORO_FEATURE_PLATFORM is actually mandatory to any example and test?

@jbaldwin
Copy link
Owner

It shouldn't be required. I recently added builds that should run on linux LIBCORO_FEATURE_PLATFORM=OFF, but its possible the windows build is exposing things I missed. Let me take a deeper look and see if its an easy fix.

@jbaldwin
Copy link
Owner

include/coro/concepts/executor.hpp only includes include/coro/concepts/awaitable.hpp which doesn't include any other coro headers, so I'm not sure I follow there?

coro::task_container does indeed forward declare io_scheduler but it doesn't include the header file.

I did look at the output from the actions job and it does seem to be linking into io_scheduler somehow, but that would mean that coro:task_container got template instantiated as coro::task_container<io_scheduler> which I'm not exactly sure where that is happening? One thing for sure we could add is ifdef's around the forward declare and the friend lines in task_container.hpp to only include them if LIBCORO_FEATURE_PLATFORM=ON

@jbaldwin
Copy link
Owner

jbaldwin commented Jan 11, 2024

I think its the coro::net::dns::resolver class, it includes io_scheduler and task_container, that class should be made to require LIBCORO_FEATURE_PLATFORM=ON.

It could also use the executor concept instead... thats probably the right thing to do. If we do that then only the example needs to be moved under PLATFORM I think

-- I'm going to try and prototype this out since I think its the right thing to do here --

@jbaldwin
Copy link
Owner

I think you're right after digging around a lot, PLATFORM and NETWORK are basically identical at this point. I'm probably just going to drop PLATFORM and use NETWORKING everywhere.

@jbaldwin
Copy link
Owner

Ok, can you try rebasing and we'll give this another go?

@uilianries
Copy link
Contributor Author

@jbaldwin sorry the delay, I was offline on last Friday. I'll rebase now.

Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
@uilianries uilianries force-pushed the feature/shared-library branch from bd125a2 to e0493b1 Compare January 15, 2024 17:04
@uilianries
Copy link
Contributor Author

Forced pushed due git rebase command. There was a conflict related to using platform option in CI. Removed as following in the main branch.

@jbaldwin
Copy link
Owner

I haven't been able to really look at why the windows build failed but is it just a missing header file? I can probably look deeper tomorrow

Signed-off-by: Uilian Ries <[email protected]>
@uilianries
Copy link
Contributor Author

@jbaldwin I just checked the build logs now and it's a limitation with CMake exporting static variables from semaphore class (coro::semaphore::acquire_result_unknown and others). We will need to export symbols using __declspec. Fortunately, CMake provides a helper for it: https://www.kitware.com/create-dlls-on-windows-without-declspec-using-new-cmake-export-all-feature/

I'll take a look later on it, right now I don't have a Windows machine to fix, only at home.

@uilianries
Copy link
Contributor Author

@jbaldwin It's working now. Some note to understand this PR at this point:

  • It generates static only or shared libraries according to LIBCORO_BUILD_SHARED_LIBS option
  • When generating shared library in Windows, it will produce bin/libcoro.dll and lib/libcoro.lib (exposed symbols to load in the dll)
  • The CMake definition CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS does not export static variables
  • To export semaphore's variables I needed to produce a new header file, the coro/export.hpp. It's generated by cmake, using the method generate_export_header, and should manage __declspec
  • The ctest does not include the DLL folder by default, so I needed to configure it by set_tests_properties

@uilianries uilianries marked this pull request as ready for review January 17, 2024 14:43
@jbaldwin
Copy link
Owner

Very nice work, thanks for sticking through on this feature!

@jbaldwin jbaldwin merged commit fbce355 into jbaldwin:main Jan 18, 2024
uilianries added a commit to uilianries/libcoro that referenced this pull request Feb 15, 2024
* CMake: Keep git hooks as optional (jbaldwin#234)

* Keep git hooks as optional since they are not required for using the library, only for development.
* Cmake LIBCORO_RUN_GITCONFIG=ON|OFF option

* Introduce concepts::io_executor (jbaldwin#238)

* Introduce concepts::io_scheduler

The dns resolver hard links against coro::io_scheduler via coro::task_container<coro::io_scheduler>. Since the concepts::executor was introduced a while ago to make it possible to pass in different executors (or even user defined executors) the resolver has apparently been forgotten about. Lets introduce an io_executor (needs poll()) and make the resolver use an agnostic concepts::executor.

The other reason for doing this is that the windows build is failing when LIBCORO_FEATURE_PLATFORM=OFF and building as a SHARED library since the coro::io_scheduler is sneaking in via the resolver class but then ultimately isn't compiled since its excluded from the build.

Closes jbaldwin#237

* Remove LIBCORO_FEATURE_PLAFTORM

It really is identical with LIBCORO_FEATURE_NETWORKING at this point, it
just wasn't so obvious.

* Add shared library option (jbaldwin#236)

* It generates static only or shared libraries according to LIBCORO_BUILD_SHARED_LIBS option via the `cmake` `BUILD_SHARED_LIBS` to propagate to all libraries built by the project.
* When generating shared library in Windows, it will produce bin/libcoro.dll and lib/libcoro.lib (exposed symbols to load in the dll)
* The CMake definition `CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS` does not export static variables
  * To export semaphore's variables a new header file is created, the `coro/export.hpp`. It's generated by `cmake` using the method `generate_export_header`, and should manage `__declspec`.
* The ctest does not include the DLL folder by default, so it is configured by using `set_tests_properties`

* Fix use after free in coro::task_container (jbaldwin#240)

In coro::task_container, this is potential for a use after free bug to
occur between the final co_return of make_cleanup_task and another
thread executing gc_internal. This can happen because there is a delay
between the std::scoped_lock being destroyed upon the co_return of
make_cleanup_task and its final suspend. This leads to a race condition
where another thread can acquire the lock and initiate a garbage
collection, where that cleanup task is destroyed before it's final
suspend. This leads to a use after free.

This commit fixes the issue by reworking how tasks get garbage
collected. Instead of unconditionally destroying tasks scheduled for
deletion, the gc_internal calls is_ready on the coro::task first. This
function will only be true in the cases where the task has entered
final suspend, has been default constructed, or has been destroyed. This
means a task can get scheduled for deletion, but not be deleted when
gc_internal is called. This allows time for the task to enter final
suspend, which avoids the use after free bug.

To make this implementation easier, several aspects of the
implementation were changed. m_task_indexes was removed in favor of
a queue which keeps track of free indices into m_tasks. This makes
things easier since it removes the hard constraint of keeping used
indices behind m_free_pos and free indices at or after.
m_tasks_to_delete was then changed to a list in order to allow for
constant time deletion of elements in place. This is helpful because it
is possible for tasks to be queue for deletion, but not yet safe to
delete. This means unconditionally clearing the m_tasks_to_delete data
structure is no longer viable.

* Upgrade tl::expected to 1.1 (jbaldwin#243)

* Creating a CI workflow for macos, (jbaldwin#249)

- Created ci-macos.yml
  - tweaked CMake to deal with clang17 location

* Fix for higher version of C++ usage, Clang support, and some build policy change. (jbaldwin#246)

* Enable test/example build only if project is top level. LIBCORO_FEATURE_NETWORKING and LIBCORO_FEATURE_TLS macro should set on Linux environment only.

* Make compilable above the C++20 mode.

* Missing include: <exception>

* Update CMakeLists.txt

* Calling coro::sync_wait with coro::ring_buffer::consume returns default constructed objects for complex return values (jbaldwin#244)

Running the test that replicates the bug via valgrind or asan it shows
that the compiler is calling sync_wait()'s promise's destructor before
moving the complex object return value. This causes the object to be
destructed and then the final move out is on deleted (use after free)
memory causing the object to be in a bad empty state.

To resolve this for now a double move has been introduced to move the
complex object off the promise object and onto the sync_wait() function
callstack. This seems to keep the object alive and not be destructed
when sync_wait() finally returns.

Other solutions could be to heap allocate the promise() object or even
the return_type but this is probably more expensive than a double move,
but will vary on use case.

Closes jbaldwin#242

* Release v0.11 (jbaldwin#250)

Closes jbaldwin#241

* Revert cmake PROJECT_IS_TOP_LEVEL (jbaldwin#252)

This only appears to work with cmake >= 3.25 which isn't ready for most
of the CI runs, so it will be reverted for now and re-worked moving
forward.

* Release v0.11.1 hotfix (jbaldwin#253)

* Added missing header '<atomic>' when compiling with clang and c++23 (jbaldwin#254)

* CI explicitly run CMAKE_CXX_STANDARD=20|23 (jbaldwin#257)

CI Added explicit 20/23 standards for:
* ci-fedora
* ci-macos
* ci-ubuntu
* ci-windows

Did not add 23 builds for
* ci-opensuse
* ci_emscripten

Closes jbaldwin#256

* Initial try to include Conan in the CI

Signed-off-by: Uilian Ries <[email protected]>

---------

Signed-off-by: Uilian Ries <[email protected]>
Co-authored-by: Josh Baldwin <[email protected]>
Co-authored-by: ripose-jp <[email protected]>
Co-authored-by: Bruno Nicoletti <[email protected]>
Co-authored-by: LEE KYOUNGHEON <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for shared library
2 participants