Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Factory
libdex
libdex-0.8.1.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File libdex-0.8.1.obscpio of Package libdex
07070100000000000081A40000000000000000000000016712D33C000000D5000000000000000000000000000000000000001B00000000libdex-0.8.1/.editorconfigroot = true [*] charset = utf-8 end_of_line = lf [*.[ch]] indent_size = 2 indent_style = space insert_final_newline = true max_line_length = 100 tab_width = 2 [meson.build] indent_size = 2 indent_style = space 07070100000001000081A40000000000000000000000016712D33C0000001D000000000000000000000000000000000000001800000000libdex-0.8.1/.gitignore*.swp *~ build/ .buildconfig 07070100000002000081A40000000000000000000000016712D33C000005C2000000000000000000000000000000000000001C00000000libdex-0.8.1/.gitlab-ci.ymlstages: - test - docs - deploy variables: FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtksourceview/fedora:latest" test: image: 'registry.fedoraproject.org/fedora:39' stage: test variables: DEPS: >- gcc gcc-c++ gettext libatomic liburing-devel meson ninja-build redhat-rpm-config glib2-devel gobject-introspection-devel git before_script: - 'cat /proc/cpuinfo' - "dnf install -y $DEPS" script: - 'meson setup _build . -Ddocs=false -Dexamples=false -Dvapi=false -Dintrospection=enabled -Dsysprof=false -Dtests=true -Dliburing=enabled -Deventfd=enabled' - 'cd _build' - 'ninja test' - 'meson configure -Dliburing=disabled -Deventfd=disabled' - 'ninja test' reference: image: $FEDORA_IMAGE stage: docs needs: [] variables: MESON_FLAGS: "-Ddocs=true -Dexamples=false -Dvapi=false -Dintrospection=enabled -Dsysprof=false -Dtests=false" script: - sudo dnf install -y libatomic liburing-devel - mkdir -p pfx/ - meson ${MESON_FLAGS} --prefix=${PWD}/pfx _build - ninja -C _build install - mkdir -p _reference/ - mv pfx/share/doc/libdex-1/ _reference/ artifacts: paths: - _reference pages: stage: deploy needs: ['reference'] script: - mv _reference public/ artifacts: paths: - public only: - main 07070100000003000081A40000000000000000000000016712D33C000067A2000000000000000000000000000000000000001500000000libdex-0.8.1/COPYING GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! 07070100000004000081A40000000000000000000000016712D33C000015E5000000000000000000000000000000000000001200000000libdex-0.8.1/NEWSlibdex 0.8.1 ============ This is a stable release for GNOME 47.0 * Fix disabling of assertions in production builds libdex 0.8.0 ============ This is a stable release for GNOME 47.0 * Various build system improvements to help catch correctness issues. * Increased assertions in fibers to help Coverity discover usage patterns. * Avoid releasing discarded future until chained future is released. * Fix overflow in extremely large timeout calculation. * Various introspection fixes. * Fix inclusion from C++. libdex 0.7.1 ============ This is an beta release for GNOME 47.beta * Allow disabling stack protector on systems that do not support it * Always use ucontext.h on macOS to simplify integration there libdex 0.7.0 ============ This is an alpha release for GNOME 47.alpha * Various build fixes for macOS 14+ * dex_future_new_true() and dex_future_new_false() convenience macros * Avoid possible linking of asm.S on Linux so that we do not make the GCC annobin checker unhappy. * Allow a NULL GObject for dex_future_new_take_object(). libdex 0.6.0 ============ This is a stable release for GNOME 46 * No changes since 0.5.1 libdex 0.5.1 ============ * Add convenience API for checking exact future status without enum * Lots of documentation additions libdex 0.5.0 ============ This is a beta release for GNOME 46. * Various API now warns about unused results to help avoid unintended leaks of futures. * dex_file_query_exists() is a new wrapper for g_file_query_exists() * Documentation fixes libdex 0.4.3 ============ This release contains a few more followup fixes for fallback scheduling, particularly on Linux systems without io_uring. We've also enabled CI to test this situation more closely as we're most likely to run into it on distribution build systems running older Linux kernels. * New build options for disabling io_uring and eventfd support which enable testing more fallback paths in CI. * Worker threads will now use a GMainLoop instead iterating GMainContext manually. * An idle is used for scheduling work items directly onto a worker thread instead of going through the global work queue when not already running on a worker thread. Otherwise the lock-free work queue is used. * A fix for a potential deadlock when failing to create an additional worker thread. * Fix a release build warning. libdex 0.4.2 ============ This release contains an important fix for running on systems with io_uring and recent Linux kernels. * io_uring AIO backend will now attempt to create the io_uring on the target worker thread so that IORING_SETUP_SINGLE_ISSUER may restrict submissions correctly. * Linux distributions may optionally disable liburing using the build time option -Dliburing=disabled. In that case, the fallback of POSIX IO with a thread-pool will be used. libdex 0.4.1 ============ This release includes an important fix for how io_uring CQE are handled. libdex 0.4.0 ============ This is the release for GNOME 45. Changes in this release: * Documentation fix libdex 0.3.1 ============ This is the beta release for GNOME 45. Changes in this release: * A new "Infinite" future type which will never resolve or reject. * The gio wrappers will now set debuggable names for their futures. * A `dex_async_pair_set_cancel_on_discard()` function has been added which ensures that the `GCancellable` will not be cancelled when the future is discarded. This can help in situations where you might repeatedly await on a future and do not want intermediate cancellation between attempts to await. * A `dex_input_stream_skip_async()` function has been added to wrap the underlying GIO function. * A `dex_future_disown()` function has been added which simplifies the case where you want a future to run but are not interested in the result. libdex 0.3.0 ============ This is the first alpha release for GNOME 45. Changes in this release: * Documentation updates * pkg-config improvements * Build system improvements for GIR * Support for GVariant-based futures * New Future-based wrappers for GDBusConnection * The FD-based AIO API is now public, allowing for use of io_uring on Linux kernels that support it. If unsupported, a threadpool approach is used similar to GIO. * Improvements to introspection for language bindings. * You can now return NULL for a DexPromise expecting a GObject as as a resolved value. * DexFiber will now get static names associated with them when used from C which points to the line of code. This is only enabled when debugging is enabled for libdex through the use of macros. libdex 0.2.0 ============ This is the first release intended for end-users. Changes in this release: * Followup fixes for building on mips * Fixes for introspection gi-scanner * Remove abstract type flag from DexFuture to satisfy GObject Introspection rules about constructors. * Many documentation/gi-annotation fixes * Build system improvements * Default to `-Dintrospection=enabled` libdex 0.1.1 ============ Changes in this release: * Fix soname versioning * Documentation improvements * Fix various GObject Introspection issues * More backports for older GLib use * Various ucontext fiber fixes for less-used architectures * Fixes for various incorrect cast-align warnings * Tweaks for better Forward portability * Add some missing g_autoptr() definitions * Add GCancellation integration to DexPromise libdex 0.1.0 ============ This is an initial release of libdex meant for experimentation and testing. 07070100000005000081A40000000000000000000000016712D33C00000BDA000000000000000000000000000000000000001700000000libdex-0.8.1/README.md# Dex Dex provides Future-based programming for GLib-based applications. It both integrates with and brings new features for application and library authors who want to structure concurrent code in an easy to manage way. Dex also provides Fibers which allow writing synchronous looking code in C that uses asynchronous and future-based APIs. Dex is licensed as LGPL-2.1+. ## Documentation You can find [documentation for Dex](https://gnome.pages.gitlab.gnome.org/libdex/libdex-1/index.html) updated as part of the CI pipeline. ## Building Dex requires GLib 2.68 or newer but can likely be ported to older versions. For those interested, you can add missing API to `dex-compat-private.h`. Some examples require additional libraries but will not be compiled if the libraries are unavailable while building. Use Meson to build the project. ```sh $ cd libdex/ $ meson setup build . --prefix=/usr $ cd build/ $ ninja $ ninja test ``` You can build for Windows using mingw which is easy on Fedora Linux. ```sh $ sudo dnf install mingw64-gcc mingw64-glib2 $ cd libdex/ $ meson setup build-win64 . --cross-file=/usr/share/mingw/toolchain-mingw64.meson $ cd build/ $ ninja # You can test using wine, but will need access to libraries $ cd /usr/x86_64-w64-mingw32/sys-root/mingw/bin/ $ wine $builddir/examples/tcp-echo.exe ``` ## Supported Platforms * Linux * macOS * FreeBSD * Windows * Illumos ## More Information You can read about why this is being created and what led to it over the past two decades of contributing to GNOME and GTK. * https://blogs.gnome.org/chergert/2022/11/24/concurrency-parallelism-i-o-scheduling-thread-pooling-and-work-stealing/ * https://blogs.gnome.org/chergert/2022/12/13/threading-fibers/ * https://blogs.gnome.org/chergert/2022/12/16/dex-examples-and-windows-support/ ## Implementation Notes While Dex is using GObject and GIO, it implements its own fundamental type (DexObject) for which all other types inherit. Given the concurrent and parallel nature of futures and the many situations to support, it is the authors opinion that the performance drawbacks of such a flexible type as GObject is too costly. By controlling the feature-set to strictly what is necessary we can avoid much of the slow paths in GObject. You wont notice much of a difference though, as types are generally defined and used very similarly to GObject's but with different macro names. You can see this elsewhere in both GStreamer and GTK 4's render nodes. ## Terminology * **Future** describes something that can either resolve (succeed) or reject (fail) now or in the future. It's resolved/rejected value is immutable after completing. * **Resolved** indicates that a future has completed successfully and provided a value which can be read by the consumer. * **Rejected** indicates that a future has completed with failure and provided an error which can be read by the consumer. * **Promise** is a **Future** that allows user code to set the resolved or rejected value once. 07070100000006000041ED0000000000000000000000026712D33C00000000000000000000000000000000000000000000001700000000libdex-0.8.1/build-aux07070100000007000081A40000000000000000000000016712D33C00000255000000000000000000000000000000000000001F00000000libdex-0.8.1/build-aux/.lcovrc# lcov and genhtml configuration # See http://ltp.sourceforge.net/coverage/lcov/lcovrc.5.php # Always enable branch coverage lcov_branch_coverage = 1 # Exclude precondition assertions, as we can never reasonably get full branch # coverage of them, as they should never normally fail. # See https://github.com/linux-test-project/lcov/issues/44 lcov_excl_br_line = LCOV_EXCL_BR_LINE|g_return_if_fail|g_return_val_if_fail|g_assert|g_assert_|g_clear_|dex_clear # Similarly for unreachable assertions. lcov_excl_line = LCOV_EXCL_LINE|g_return_if_reached|g_return_val_if_reached|g_assert_not_reached 07070100000008000081ED0000000000000000000000016712D33C000001B0000000000000000000000000000000000000002300000000libdex-0.8.1/build-aux/coverage.sh#!/bin/sh mkdir -p "${BUILDDIR}/coverage" ninja -C "${BUILDDIR}" lcov --config-file "${SRCDIR}/build-aux/.lcovrc" --directory "${BUILDDIR}" --capture --initial --output-file "${BUILDDIR}/coverage/dex.lcov" ninja -C "${BUILDDIR}" test lcov --config-file "${SRCDIR}/build-aux/.lcovrc" --directory "${BUILDDIR}" --capture --output-file "${BUILDDIR}/coverage/dex.lcov" genhtml -o "${BUILDDIR}/coverage" "${BUILDDIR}/coverage/dex.lcov" 07070100000009000041ED0000000000000000000000026712D33C00000000000000000000000000000000000000000000001200000000libdex-0.8.1/docs0707010000000A000081A40000000000000000000000016712D33C0000048B000000000000000000000000000000000000001E00000000libdex-0.8.1/docs/Dex.toml.in[library] version = "@version@" browse_url = "https://gitlab.gnome.org/GNOME/libdex/" repository_url = "https://gitlab.gnome.org/GNOME/libdex.git" website_url = "https://gitlab.gnome.org/GNOME/libdex/" docs_url = "https://gnome.pages.gitlab.gnome.org/libdex/" authors = "Christian Hergert" license = "LGPL-2.1-or-later" description = "A GNOME library for deferred execution" dependencies = ["Gio-2.0"] devhelp = true search_index = true [dependencies."GObject-2.0"] name = "GObject" description = "The base type system library" docs_url = "https://docs.gtk.org/gobject/" [dependencies."GLib-2.0"] name = "GLib" description = "The base type system library" docs_url = "https://docs.gtk.org/glib/" [dependencies."Gio-2.0"] name = "GIO" description = "GObject Interfaces and Objects, Networking, IPC, and I/O" docs_url = "https://docs.gtk.org/gio/" [theme] name = "basic" show_index_summary = true show_class_hierarchy = true [source-location] base_url = "https://gitlab.gnome.org/GNOME/libdex/-/blob/main/" [extra] # The same order will be used when generating the index content_files = [ "overview.md", ] urlmap_file = "urlmap.js" 0707010000000B000081A40000000000000000000000016712D33C000003D0000000000000000000000000000000000000001E00000000libdex-0.8.1/docs/meson.buildif not get_option('introspection').allowed() error('API reference requires introspection.') endif toml_conf = configuration_data() toml_conf.set('version', meson.project_version()) gidocgen = find_program('gi-docgen') gidocgen_common_args = [ '--quiet', '--no-namespace-dir', ] if get_option('werror') gidocgen_common_args += ['--fatal-warnings'] endif docs_dir = get_option('datadir') / 'doc' source_toml = configure_file( input: 'Dex.toml.in', output: 'Dex.toml', configuration: toml_conf, install: true, install_dir: docs_dir / 'libdex-@0@'.format(api_version), ) custom_target('dex-doc', input: [ source_toml, libdex_gir[0] ], output: 'libdex-@0@'.format(api_version), command: [ gidocgen, 'generate', gidocgen_common_args, '--config=@INPUT0@', '--output-dir=@OUTPUT@', '--content-dir=@0@'.format(meson.current_source_dir()), '@INPUT1@', ], build_by_default: true, install: true, install_dir: docs_dir, ) 0707010000000C000081A40000000000000000000000016712D33C0000028C000000000000000000000000000000000000001E00000000libdex-0.8.1/docs/overview.mdTitle: Overview # Overview Dex is a [GNOME](https://www.gnome.org/) library that provides deferred execution for GObject-based (including GTK) applications through the use of futures. The library attempts to follow well established patterns and terminology around programming with futures. Dex depends on modern releases of GLib 2.0, but can probably be made to support older versions depending if people show up to test it. ## pkg-config name To build a program that uses Dex, you can use the following command to get the cflags and libraries necessary to compile and link. ```sh gcc hello.c `pkg-config --cflags --libs libdex-1` -o hello ``` 0707010000000D000081A40000000000000000000000016712D33C000000E7000000000000000000000000000000000000001C00000000libdex-0.8.1/docs/urlmap.js // A map between namespaces and base URLs for their online documentation baseURLs = [ [ 'GLib', 'https://docs.gtk.org/glib/' ], [ 'GObject', 'https://docs.gtk.org/gobject/' ], [ 'Gio', 'https://docs.gtk.org/gio/' ], ] 0707010000000E000041ED0000000000000000000000026712D33C00000000000000000000000000000000000000000000001600000000libdex-0.8.1/examples0707010000000F000081A40000000000000000000000016712D33C00000F4F000000000000000000000000000000000000002000000000libdex-0.8.1/examples/cat-aio.c/* * cat-aio.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <unistd.h> #include "cat-util.h" /* NOTE: * * `cat` from coreutils is certainly faster than this, especially if you're * doing things like `./examples/cat foo > bar` as it will use * copy_file_range() to avoid reading into userspace. * * `gio cat` is likely faster than this doing synchronous IO on the calling * thread because it doesn't have to coordinate across thread pools. */ static DexFuture * cat_read_fiber (gpointer user_data) { Cat *cat = user_data; Buffer *buffer = NULL; for (;;) { Buffer *next; /* Suspend while sending the buffer to the channel, which may * help throttle reads if they get too far ahead of writes. */ if (buffer != NULL) dex_await (dex_channel_send (cat->channel, dex_future_new_for_pointer (g_steal_pointer (&buffer))), NULL); /* Get next buffer from the pool */ next = cat_pop_buffer (cat); /* Suspend while reading into the buffer */ next->length = dex_await_int64 (dex_aio_read (NULL, cat->read_fd, next->data, next->capacity, -1), NULL); /* If we got length <= 0, we failed or finished */ if (next->length <= 0) { dex_channel_close_send (cat->channel); cat_push_buffer (cat, next); break; } buffer = next; } return dex_future_new_for_boolean (TRUE); } static DexFuture * cat_write_fiber (gpointer user_data) { Cat *cat = user_data; for (;;) { Buffer *buffer; gssize len; /* Suspend while we wait for another buffer (or error on channel closed) */ if (!(buffer = dex_await_pointer (dex_channel_receive (cat->channel), NULL))) break; /* Suspend while we write the buffer contents to output stream */ len = dex_await_int64 (dex_aio_write (NULL, cat->write_fd, buffer->data, buffer->length, -1), NULL); /* Give the buffer back to the pool */ cat_push_buffer (cat, buffer); /* Bail if we got a failure or empty buffer */ if (len <= 0) break; } return dex_future_new_for_boolean (TRUE); } int main (int argc, char *argv[]) { GError *error = NULL; Cat cat; int ret = EXIT_SUCCESS; dex_init (); if (!cat_init (&cat, &argc, &argv, &error) || !cat_run (&cat, dex_scheduler_spawn (NULL, 0, cat_read_fiber, &cat, NULL), dex_scheduler_spawn (NULL, 0, cat_write_fiber, &cat, NULL), &error)) { g_printerr ("cat: %s\n", error->message); g_clear_error (&error); ret = EXIT_FAILURE; } cat_clear (&cat); return ret; } 07070100000010000081A40000000000000000000000016712D33C00001A94000000000000000000000000000000000000002100000000libdex-0.8.1/examples/cat-util.h/* cat-util.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <errno.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <glib/gstdio.h> #include <libdex.h> /* You don't need to do this in your code. This just allows us to build * Dex on older systems where GLib features we want to use are not available * such as aligned allocations. */ #include "dex-compat-private.h" G_BEGIN_DECLS typedef struct _Cat Cat; typedef struct _Buffer Buffer; struct _Cat { gsize buffer_size; int read_fd; int write_fd; gssize to_read; GQueue buffer_pool; DexChannel *channel; GMainLoop *main_loop; GError *error; Buffer *current; }; struct _Buffer { Cat *cat; GList link; gpointer data; gsize capacity; gssize length; }; static inline Buffer * buffer_new (Cat *cat) { Buffer *buffer = g_new0 (Buffer, 1); buffer->cat = cat; buffer->link.data = buffer; buffer->data = g_aligned_alloc (1, cat->buffer_size, 4096); buffer->capacity = cat->buffer_size; buffer->length = 0; return buffer; } static inline void buffer_free (Buffer *buffer) { g_aligned_free (buffer->data); g_free (buffer); } static inline gboolean cat_error (GError **error, int errsv) { g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv), g_strerror (errsv)); return FALSE; } static inline gboolean cat_init (Cat *cat, int *argc, char ***argv, GError **error) { GOptionContext *context; char *output = NULL; gboolean ret = FALSE; int buffer_size = (1024*256 - 2*sizeof(gpointer)); /* 256k minus malloc overhead */ int queue_size = 32; #ifdef HAVE_POSIX_FADVISE gssize len; #endif struct stat stbuf; const GOptionEntry entries[] = { { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output, "Cat contents into OUTPUT", "OUTPUT" }, { "buffer-size", 'b', 0, G_OPTION_ARG_INT, &buffer_size, "Read/Write buffer size", "BYTES" }, { "queue-size", 'q', 0, G_OPTION_ARG_INT, &queue_size, "Amount of reads that can advance ahead of writes (default 32)", "COUNT" }, { 0 } }; memset (cat, 0, sizeof *cat); cat->buffer_size = buffer_size; cat->read_fd = -1; cat->write_fd = -1; cat->channel = dex_channel_new (queue_size); cat->main_loop = g_main_loop_new (NULL, FALSE); context = g_option_context_new ("- FILE"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, argc, argv, error)) goto cleanup; cat->write_fd = STDOUT_FILENO; if (output != NULL) { g_unlink (output); if (-1 == (cat->write_fd = open (output, O_WRONLY|O_CREAT, 0644))) goto cleanup; } if (*argc > 2) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVAL, "Only a single file is supported"); goto cleanup; } else if (*argc == 2) { if (-1 == (cat->read_fd = open ((*argv)[1], O_RDONLY))) goto cleanup; } else { cat->read_fd = STDIN_FILENO; } #ifdef HAVE_POSIX_FADVISE len = lseek (cat->read_fd, 0, SEEK_END); if (len > 0) { posix_fadvise (cat->read_fd, 0, len, POSIX_FADV_SEQUENTIAL); posix_fadvise (cat->write_fd, 0, len, POSIX_FADV_SEQUENTIAL); lseek (cat->read_fd, 0, SEEK_SET); } #endif if (fstat (cat->read_fd, &stbuf) == 0) cat->to_read = stbuf.st_size; else cat->to_read = -1; ret = TRUE; cleanup: if (ret == FALSE) { int errsv = errno; char *help = g_option_context_get_help (context, TRUE, NULL); g_printerr ("%s\n", help); g_free (help); if (error && !*error) g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv), g_strerror (errsv)); } g_option_context_free (context); g_free (output); return ret; } static inline void cat_clear (Cat *cat) { if (cat->write_fd >= 0) close (cat->write_fd); if (cat->read_fd >= 0) close (cat->read_fd); while (cat->buffer_pool.head != NULL) { Buffer *buffer = cat->buffer_pool.head->data; g_queue_unlink (&cat->buffer_pool, &buffer->link); buffer_free (buffer); } dex_clear (&cat->channel); g_clear_pointer (&cat->current, buffer_free); g_clear_pointer (&cat->main_loop, g_main_loop_unref); } static inline Buffer * cat_pop_buffer (Cat *cat) { if (cat->buffer_pool.length == 0) return buffer_new (cat); return g_queue_pop_head_link (&cat->buffer_pool)->data; } static inline void cat_push_buffer (Cat *cat, Buffer *buffer) { g_assert (cat != NULL); g_assert (buffer != NULL); g_assert (buffer->link.data == buffer); g_assert (buffer->link.prev == NULL); g_assert (buffer->link.next == NULL); buffer->length = 0; g_queue_push_head_link (&cat->buffer_pool, &buffer->link); } static inline DexFuture * cat_run_cb (DexFuture *future, gpointer user_data) { Cat *cat = user_data; dex_future_get_value (future, &cat->error); g_main_loop_quit (cat->main_loop); return NULL; } static inline DexFuture * cat_close_send (DexFuture *future, gpointer user_data) { Cat *cat = user_data; dex_channel_close_send (cat->channel); return NULL; } static inline gboolean cat_run (Cat *cat, DexFuture *read_routine, DexFuture *write_routine, GError **error) { DexFuture *future; future = dex_future_finally (read_routine, cat_close_send, cat, NULL); future = dex_future_all (future, write_routine, NULL); future = dex_future_finally (future, cat_run_cb, cat, NULL); g_main_loop_run (cat->main_loop); dex_unref (future); if (cat->error) { g_propagate_error (error, g_steal_pointer (&cat->error)); return FALSE; } return TRUE; } G_END_DECLS 07070100000011000081A40000000000000000000000016712D33C000011AC000000000000000000000000000000000000001C00000000libdex-0.8.1/examples/cat.c/* * cat.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <unistd.h> #include <gio/gio.h> #include <gio/gunixinputstream.h> #include <gio/gunixoutputstream.h> #include "cat-util.h" /* NOTE: * * `cat` from coreutils is certainly faster than this, especially if you're * doing things like `./examples/cat foo > bar` as it will use * copy_file_range() to avoid reading into userspace. * * `gio cat` is likely faster than this doing synchronous IO on the calling * thread because it doesn't have to coordinate across thread pools. */ static DexFuture * cat_read_fiber (gpointer user_data) { Cat *cat = user_data; GInputStream *stream = g_unix_input_stream_new (cat->read_fd, FALSE); Buffer *buffer = NULL; for (;;) { Buffer *next; /* Suspend while sending the buffer to the channel, which may * help throttle reads if they get too far ahead of writes. */ if (buffer != NULL) dex_await (dex_channel_send (cat->channel, dex_future_new_for_pointer (g_steal_pointer (&buffer))), NULL); /* Get next buffer from the pool */ next = cat_pop_buffer (cat); /* Suspend while reading into the buffer */ next->length = dex_await_int64 (dex_input_stream_read (stream, next->data, next->capacity, G_PRIORITY_DEFAULT), NULL); /* If we got length <= 0, we failed or finished */ if (next->length <= 0) { dex_channel_close_send (cat->channel); cat_push_buffer (cat, next); break; } buffer = next; } /* Suspend while we close the stream */ dex_await (dex_input_stream_close (stream, 0), NULL); g_object_unref (stream); return dex_future_new_for_boolean (TRUE); } static DexFuture * cat_write_fiber (gpointer user_data) { Cat *cat = user_data; GOutputStream *stream = g_unix_output_stream_new (cat->write_fd, FALSE); for (;;) { Buffer *buffer; gssize len; /* Suspend while we wait for another buffer (or error on channel closed) */ if (!(buffer = dex_await_pointer (dex_channel_receive (cat->channel), NULL))) break; /* Suspend while we write the buffer contents to output stream */ len = dex_await_int64 (dex_output_stream_write (G_OUTPUT_STREAM (stream), buffer->data, buffer->length, G_PRIORITY_DEFAULT), NULL); /* Give the buffer back to the pool */ cat_push_buffer (cat, buffer); /* Bail if we got a failure or empty buffer */ if (len <= 0) break; } /* Asynchronously close the stream, which may cause flushing to occur. */ dex_await (dex_output_stream_close (stream, 0), NULL); /* Release the stream, which should not block since it was closed above */ g_object_unref (stream); return dex_future_new_for_boolean (TRUE); } int main (int argc, char *argv[]) { GError *error = NULL; Cat cat; int ret = EXIT_SUCCESS; dex_init (); if (!cat_init (&cat, &argc, &argv, &error) || !cat_run (&cat, dex_scheduler_spawn (NULL, 0, cat_read_fiber, &cat, NULL), dex_scheduler_spawn (NULL, 0, cat_write_fiber, &cat, NULL), &error)) { g_printerr ("cat: %s\n", error->message); g_clear_error (&error); ret = EXIT_FAILURE; } cat_clear (&cat); return ret; } 07070100000012000081A40000000000000000000000016712D33C00002016000000000000000000000000000000000000001B00000000libdex-0.8.1/examples/cp.c/* cp.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> #include <unistd.h> #define return_if_error(error) \ G_STMT_START { \ if (error) \ return dex_future_new_for_error (error); \ } G_STMT_END static DexFuture *copy (gpointer user_data); static DexScheduler *thread_pool; static gboolean verbose; static GMainLoop *main_loop; typedef struct { GFile *from; GFile *to; guint recursive : 1; } Copy; static Copy * copy_new (GFile *from, GFile *to, gboolean recursive) { Copy *cp = g_new0 (Copy, 1); cp->from = from; /* take ownership */ cp->to = to; /* take ownership */ cp->recursive = !!recursive; return cp; } static void copy_free (Copy *cp) { g_clear_object (&cp->from); g_clear_object (&cp->to); g_free (cp); } static DexFuture * copy_regular (Copy *cp) { g_autoptr(GInputStream) input = NULL; g_autoptr(GOutputStream) output = NULL; GError *error = NULL; G_GNUC_UNUSED gssize len; if (verbose) g_printerr ("%s => %s\n", g_file_peek_path (cp->from), g_file_peek_path (cp->to)); input = dex_await_object (dex_file_read (cp->from, G_PRIORITY_DEFAULT), &error); return_if_error (error); output = dex_await_object (dex_file_replace (cp->to, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, G_PRIORITY_DEFAULT), &error); return_if_error (error); len = dex_await_int64 (dex_output_stream_splice (output, input, (G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET), G_PRIORITY_DEFAULT), &error); return_if_error (error); return dex_future_new_for_boolean (TRUE); } static DexFuture * copy_directory (Copy *cp) { g_autoptr(GFileEnumerator) enumerator = NULL; g_autoptr(GPtrArray) futures = g_ptr_array_new_with_free_func (dex_unref); GError *error = NULL; enumerator = dex_await_object (dex_file_enumerate_children (cp->from, G_FILE_ATTRIBUTE_STANDARD_NAME",", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, G_PRIORITY_DEFAULT), &error); return_if_error (error); if (verbose) g_printerr ("%s/ => %s/\n", g_file_peek_path (cp->from), g_file_peek_path (cp->to)); dex_await (dex_file_make_directory (cp->to, G_PRIORITY_DEFAULT), &error); return_if_error (error); for (;;) { g_autolist(GFileInfo) files = NULL; files = dex_await_boxed (dex_file_enumerator_next_files (enumerator, 100, G_PRIORITY_DEFAULT), &error); return_if_error (error); if (files == NULL) break; for (const GList *iter = files; iter; iter = iter->next) { GFileInfo *info = iter->data; g_ptr_array_add (futures, dex_scheduler_spawn (thread_pool, 0, copy, copy_new (g_file_enumerator_get_child (enumerator, info), g_file_get_child (cp->to, g_file_info_get_name (info)), cp->recursive), (GDestroyNotify) copy_free)); } } if (futures->len > 0) { dex_await (dex_future_allv ((DexFuture **)(gpointer)futures->pdata, futures->len), &error); return_if_error (error); } return dex_future_new_for_boolean (TRUE); } static DexFuture * copy_fallback (Copy *cp) { if (verbose) g_print ("%s => %s\n", g_file_peek_path (cp->from), g_file_peek_path (cp->to)); /* Fallback to internal GIO copying semantics, which can't handle * directories or things of that nature. * * This returns a future, which the fiber scheduler will yield for * us as part of fiber completion. */ return dex_file_copy (cp->from, cp->to, G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA, G_PRIORITY_DEFAULT); } static DexFuture * copy (gpointer user_data) { Copy *cp = user_data; g_autoptr(GFileInfo) info = NULL; GFileType file_type; GError *error = NULL; info = dex_await_object (dex_file_query_info (cp->from, G_FILE_ATTRIBUTE_STANDARD_SIZE"," G_FILE_ATTRIBUTE_STANDARD_TYPE"," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK"," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET",", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, G_PRIORITY_DEFAULT), &error); return_if_error (error); file_type = g_file_info_get_file_type (info); if (file_type == G_FILE_TYPE_REGULAR) return copy_regular (cp); if (file_type == G_FILE_TYPE_DIRECTORY) { if (!cp->recursive) return dex_future_new_reject (G_IO_ERROR, G_IO_ERROR_FAILED, "%s is a directory and -r is not set", g_file_peek_path (cp->from)); return copy_directory (cp); } return copy_fallback (cp); } static DexFuture * quit_cb (DexFuture *future, gpointer user_data) { GError *error = NULL; if (!dex_await (dex_ref (future), &error)) g_error ("%s", error->message); g_main_loop_quit (main_loop); return NULL; } int main (int argc, char *argv[]) { gboolean recursive = FALSE; GOptionEntry entries[] = { { "recursive", 'r', 0, G_OPTION_ARG_NONE, &recursive, "Copy directory recursively" }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Explain what is being done" }, { NULL } }; GOptionContext *context; DexFuture *future; GError *error = NULL; dex_init (); context = g_option_context_new ("[OPTIONS...] SOURCE DEST - copy files"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) g_error ("%s", error->message); if (argc != 3) { g_printerr ("usage: %s SOURCE DEST\n", argv[0]); return EXIT_FAILURE; } main_loop = g_main_loop_new (NULL, FALSE); thread_pool = dex_thread_pool_scheduler_new (); future = dex_scheduler_spawn (NULL, 0, copy, copy_new (g_file_new_for_commandline_arg (argv[1]), g_file_new_for_commandline_arg (argv[2]), recursive), (GDestroyNotify)copy_free); future = dex_future_finally (future, quit_cb, NULL, NULL); g_main_loop_run (main_loop); dex_unref (future); g_option_context_free (context); g_main_loop_unref (main_loop); return EXIT_SUCCESS; } 07070100000013000081A40000000000000000000000016712D33C00001DDA000000000000000000000000000000000000002300000000libdex-0.8.1/examples/echo-bench.c/* echo-bench.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> typedef struct _Worker { gint64 conn_attempts; gint64 conn_failures; gint64 conn_success; gint64 conn_closed; gint64 bytes_sent; gint64 bytes_received; } Worker; static DexScheduler *thread_pool; static GSocketConnectable *socket_address; static gchar *buf; static gsize buflen; static GPtrArray *fibers; static Worker *workers; static guint n_workers; static gboolean in_shutdown; static GTimer *timer; static DexFuture * worker_fiber (gpointer user_data) { g_autoptr(GSocketClient) client = NULL; Worker *worker = user_data; g_autofree char *inbuf = g_malloc (buflen); client = g_socket_client_new (); while (!g_atomic_int_get (&in_shutdown)) { g_autoptr(GSocketConnection) connection = NULL; GOutputStream *output; GInputStream *input; gssize len; worker->conn_attempts++; if (!(connection = dex_await_object (dex_socket_client_connect (client, socket_address), NULL))) { worker->conn_failures++; break; } worker->conn_success++; output = g_io_stream_get_output_stream (G_IO_STREAM (connection)); input = g_io_stream_get_input_stream (G_IO_STREAM (connection)); if ((len = dex_await_int64 (dex_output_stream_write (output, buf, buflen, G_PRIORITY_DEFAULT), NULL)) <= 0) break; worker->bytes_sent += len; if ((len = dex_await_int64 (dex_input_stream_read (input, inbuf, buflen, G_PRIORITY_DEFAULT), NULL)) <= 0) break; worker->bytes_received += len; dex_await (dex_io_stream_close (G_IO_STREAM (connection), G_PRIORITY_DEFAULT), NULL); } return NULL; } static DexFuture * shutdown_cb (DexFuture *completed, gpointer user_data) { /* Signal to workers they should complete */ g_atomic_int_set (&in_shutdown, TRUE); /* No need to wait for workers */ return NULL; } static DexFuture * quit_cb (DexFuture *completed, gpointer user_data) { GMainLoop *main_loop = user_data; g_main_loop_quit (main_loop); return NULL; } static gboolean print_live_status (gpointer data) { Worker total = {0}; double duration = g_timer_elapsed (timer, NULL); g_autofree char *sent_per_sec = NULL; g_autofree char *recv_per_sec = NULL; for (guint i = 0; i < n_workers; i++) { total.conn_attempts += workers[i].conn_attempts; total.conn_failures += workers[i].conn_failures; total.conn_success += workers[i].conn_success; total.conn_closed += workers[i].conn_closed; total.bytes_sent += workers[i].bytes_sent; total.bytes_received += workers[i].bytes_received; } sent_per_sec = g_format_size (total.bytes_sent/duration); recv_per_sec = g_format_size (total.bytes_received/duration); g_printerr ("\n"); g_printerr (" req: succ=%"G_GINT64_FORMAT" (per-sec %0.2lf) fail=%"G_GINT64_FORMAT" (per-sec=%0.2lf)\n", total.conn_success, total.conn_success/duration, total.conn_failures, total.conn_failures/duration); g_printerr ("bytes: sent=%"G_GINT64_FORMAT" (per-sec %s) recv=%"G_GINT64_FORMAT" (per-sec %s)\n", total.bytes_sent, sent_per_sec, total.bytes_received, recv_per_sec); return G_SOURCE_CONTINUE; } static void print_results (void) { print_live_status (NULL); } int main (int argc, char *argv[]) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GMainLoop) main_loop = NULL; g_autoptr(DexFuture) future = NULL; g_autoptr(GError) error = NULL; g_autofree char *address = NULL; g_autofree char *message = NULL; int length = 0; int duration = 0; int number = 0; GOptionEntry entries[] = { { "address", 'a', 0, G_OPTION_ARG_STRING, &address, "Target echo server adderss.", "0.0.0.0:8080" }, { "length", 'l', 0, G_OPTION_ARG_INT, &length, "Target message length.", "BYTES" }, { "duration", 'd', 0, G_OPTION_ARG_INT, &duration, "Test duration in seconds.", "SECONDS" }, { "number", 'c', 0, G_OPTION_ARG_INT, &number, "Number of concurrent connections.", "CONNECTIONS" }, { "message", 'm', 0, G_OPTION_ARG_STRING, &message, "A custom message to send.", "MSG" }, { NULL } }; context = g_option_context_new ("- Simple echo benchmark client"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error) || address == NULL || !(socket_address = g_network_address_parse (address, 8080, &error))) { g_autofree char *help = g_option_context_get_help (context, TRUE, NULL); if (error != NULL) g_printerr ("%s\n\n", error->message); g_printerr ("%s\n", help); return EXIT_FAILURE; } /* Setup some defaults */ if (duration <= 0) duration = 60; if (number <= 0) number = 1000; if (length <= 0) length = 512; /* Rewrite the address to show the user what we think we parsed */ g_free (address); address = g_socket_connectable_to_string (socket_address); /* The message to send */ if (message != NULL) { buf = g_steal_pointer (&message); buflen = strlen (buf); length = buflen; g_printerr ("Using custom message:\n\n" "================================\n" "%s\n" "================================\n", buf); } else { buflen = length; buf = g_strnfill (buflen, 'X'); } g_printerr ("Benchmarking: %s\n", address); g_printerr ("%u clients, running %u bytes, %u sec.\n", number, length, duration); /* Space for the workers to track information */ n_workers = number; workers = g_new0 (Worker, n_workers); /* Setup main loop for this thread and thread pool for the others * where we'll push fibers for clients. */ main_loop = g_main_loop_new (NULL, FALSE); thread_pool = dex_thread_pool_scheduler_new (); timer = g_timer_new (); /* Hold a reference to the fibers so we can join them */ fibers = g_ptr_array_new_with_free_func (dex_unref); for (int i = 0; i < number; i++) g_ptr_array_add (fibers, dex_scheduler_spawn (thread_pool, 0, worker_fiber, &workers[i], NULL)); /* After @duration seconds, reject */ future = dex_timeout_new_seconds (duration); /* Handle that by shutting down somewhat gracefully */ future = dex_future_finally (future, shutdown_cb, NULL, NULL); /* When that completes, quit the main loop */ future = dex_future_finally (future, quit_cb, main_loop, NULL); /* Periodically print out worker status */ g_timeout_add_seconds (1, print_live_status, NULL); g_main_loop_run (main_loop); print_results (); /* Cleanup state */ g_object_unref (socket_address); g_ptr_array_unref (fibers); g_free (workers); g_free (buf); return EXIT_SUCCESS; } 07070100000014000081A40000000000000000000000016712D33C0000094C000000000000000000000000000000000000001D00000000libdex-0.8.1/examples/host.c/* host.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> static DexFuture * resolve_address (gpointer user_data) { GResolver *resolver = g_resolver_get_default (); const char *addr = user_data; g_autoptr(GError) error = NULL; g_autolist(GInetAddress) addrs = NULL; addrs = dex_await_boxed (dex_resolver_lookup_by_name (resolver, addr), &error); if (error != NULL) { g_printerr ("%s: %s\n", addr, error->message); return NULL; } for (const GList *iter = addrs; iter; iter = iter->next) { GInetAddress *inet_address = iter->data; g_autofree char *str = g_inet_address_to_string (inet_address); g_print ("%s has address %s\n", addr, str); } return NULL; } static DexFuture * quit_cb (DexFuture *completed, gpointer user_data) { GMainLoop *main_loop = user_data; g_main_loop_quit (main_loop); return NULL; } int main (int argc, char *argv[]) { g_autoptr(GMainLoop) main_loop = g_main_loop_new (NULL, FALSE); g_autoptr(GPtrArray) futures = g_ptr_array_new (); g_autoptr(DexFuture) future = NULL; dex_init (); if (argc < 2) { g_printerr ("usage: %s HOSTNAME...\n", argv[0]); return 1; } for (guint i = 1; i < argc; i++) { const char *addr = argv[i]; g_ptr_array_add (futures, dex_scheduler_spawn (NULL, 0, resolve_address, g_strdup (addr), g_free)); } future = dex_future_allv ((DexFuture **)futures->pdata, futures->len); future = dex_future_finally (future, quit_cb, main_loop, NULL); g_main_loop_run (main_loop); return 0; } 07070100000015000081A40000000000000000000000016712D33C000024B5000000000000000000000000000000000000001E00000000libdex-0.8.1/examples/httpd.c/* httpd.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> #include <libsoup/soup.h> #include <unistd.h> static const char *errbody = "<html><body><h1>Not Found</h1></body></html>"; typedef struct { SoupServer *server; SoupServerMessage *message; char *path; GHashTable *query; } Request; static void request_free (gpointer data) { Request *request = data; g_clear_pointer (&request->path, g_free); g_clear_pointer (&request->query, g_hash_table_unref); g_clear_object (&request->message); g_clear_object (&request->server); g_free (request); } static void request_pause (Request *request) { #if SOUP_CHECK_VERSION(3, 2, 0) soup_server_message_pause (request->message); #else soup_server_pause_message (request->server, request->message); #endif } static void request_unpause (Request *request) { #if SOUP_CHECK_VERSION(3, 2, 0) soup_server_message_unpause (request->message); #else soup_server_unpause_message (request->server, request->message); #endif } static DexFuture * request_fiber (gpointer user_data) { Request *request = user_data; SoupMessageHeaders *headers; const char *path = request->path; GFileInfo *file_info; GError *error = NULL; GFile *file; GFileType file_type; while (path[0] == '/') path++; if (path[0] == 0) path = "."; file = g_file_new_for_path (path); file_info = dex_await_object (dex_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," G_FILE_ATTRIBUTE_STANDARD_TYPE"," G_FILE_ATTRIBUTE_STANDARD_SIZE",", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, G_PRIORITY_DEFAULT), &error); if (file_info != NULL) file_type = g_file_info_get_file_type (file_info); else file_type = G_FILE_TYPE_UNKNOWN; headers = soup_server_message_get_response_headers (request->message); if (soup_server_message_get_method (request->message) == SOUP_METHOD_HEAD) { char *length = g_strdup_printf ("%"G_GOFFSET_FORMAT, g_file_info_get_size (file_info)); soup_message_headers_append (headers, "Content-Length", length); soup_server_message_set_status (request->message, SOUP_STATUS_OK, NULL); g_free (length); goto unpause; } /* TODO: It would be nice to use io_uring_prep_openat() via the * AIO backend here so that we can multiplex without hitting the * GIO async aio threadpool. That will require additions to * DexAioBackend to accomidate it, but would save us potentially * clogging the GIO pool as well as avoiding blocking on open() * in a fiber/worker. */ if (file_type == G_FILE_TYPE_REGULAR) { SoupMessageBody *body = soup_server_message_get_response_body (request->message); goffset to_read = g_file_info_get_size (file_info); GInputStream *stream; gsize buflen = to_read; if (to_read > 4096*16) buflen = 1024*64 - 2*GLIB_SIZEOF_VOID_P; if (!(stream = dex_await_object (dex_file_read (file, G_PRIORITY_DEFAULT), &error))) goto respond_404; soup_message_headers_set_encoding (headers, SOUP_ENCODING_CHUNKED); soup_server_message_set_status (request->message, SOUP_STATUS_OK, NULL); for (;;) { guint8 *buffer = g_malloc (buflen); gssize len; len = dex_await_int64 (dex_input_stream_read (stream, buffer, MIN (to_read, buflen), G_PRIORITY_DEFAULT), &error); if (error != NULL || len <= 0) break; soup_message_body_append (body, SOUP_MEMORY_TAKE, buffer, len); to_read -= len; if (to_read <= 0) break; } soup_message_body_complete (body); } else if (file_type == G_FILE_TYPE_DIRECTORY) { GFileEnumerator *enumerator; GString *body; GList *files; gsize len; if (!(enumerator = dex_await_object (dex_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," G_FILE_ATTRIBUTE_STANDARD_SIZE",", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, G_PRIORITY_DEFAULT), &error))) goto respond_404; soup_server_message_set_status (request->message, SOUP_STATUS_OK, NULL); body = g_string_new (NULL); g_string_append_printf (body, "<html><body><h1>/%s</h1><ul>", g_str_equal (path, ".") ? "" : path); while ((files = dex_await_boxed (dex_file_enumerator_next_files (enumerator, 100, G_PRIORITY_DEFAULT), &error))) { for (const GList *iter = files; iter; iter = iter->next) { GFileInfo *info = iter->data; goffset size = g_file_info_get_size (info); g_string_append_printf (body, "<li><a href=\"%s/%s\">%s%s</a> - %"G_GOFFSET_FORMAT"</li>\n", path, g_file_info_get_name (info), g_file_info_get_display_name (info), g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY ? "/" : "", size); g_object_unref (info); } g_list_free (files); } g_string_append (body, "</h1></body></html>"); len = body->len; soup_server_message_set_response (request->message, "text/html", SOUP_MEMORY_TAKE, g_string_free (body, FALSE), len); } else { respond_404: soup_server_message_set_response (request->message, "text/html", SOUP_MEMORY_COPY, errbody, strlen (errbody)); } unpause: request_unpause (request); g_clear_object (&file_info); g_clear_object (&file); g_clear_error (&error); return NULL; } static void handle_message (SoupServer *server, SoupServerMessage *message, const char *path, GHashTable *query, gpointer user_data) { Request *request; const char *method; g_assert (SOUP_IS_SERVER (server)); g_assert (SOUP_IS_SERVER_MESSAGE (message)); g_assert (path != NULL); method = soup_server_message_get_method (message); if (method != SOUP_METHOD_GET && method != SOUP_METHOD_HEAD) { soup_server_message_set_status (message, SOUP_STATUS_NOT_IMPLEMENTED, NULL); return; } request = g_new0 (Request, 1); request->server = g_object_ref (server); request->message = g_object_ref (message); request->path = g_strdup (path); request->query = query ? g_hash_table_ref (query) : NULL; request_pause (request); dex_future_disown (dex_scheduler_spawn (NULL, 0, request_fiber, request, request_free)); } static void print_uris (SoupServer *server) { GSList *uris; g_assert (SOUP_IS_SERVER (server)); if ((uris = soup_server_get_uris (server))) { for (const GSList *iter = uris; iter; iter = iter->next) { GUri *uri = iter->data; char *str = g_uri_to_string (uri); g_printerr ("Listening on %s\n", str); g_free (str); } g_slist_free_full (g_steal_pointer (&uris), (GDestroyNotify)g_uri_unref); } } int main (int argc, char *argv[]) { SoupServer *server; GMainLoop *main_loop; GError *error = NULL; dex_init (); main_loop = g_main_loop_new (NULL, FALSE); server = soup_server_new ("server-header", "libdex-httpd", NULL); soup_server_add_handler (server, "/", handle_message, NULL, NULL); if (!soup_server_listen_all (server, 8080, 0, &error)) g_error ("%s", error->message); print_uris (server); g_main_loop_run (main_loop); g_main_loop_unref (main_loop); g_object_unref (server); return EXIT_SUCCESS; } 07070100000016000081A40000000000000000000000016712D33C00000833000000000000000000000000000000000000002600000000libdex-0.8.1/examples/infinite-loop.c/* infinite-loop.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <libdex.h> static DexFuture * infinite_loop_cb (DexFuture *future, gpointer user_data) { const GValue *value; GMainLoop *main_loop = user_data; GError *error = NULL; if ((value = dex_future_get_value (future, &error))) { g_print ("\nCaught signal %u, exiting.\n", g_value_get_int (value)); g_main_loop_quit (main_loop); return NULL; } g_print ("Looping ...\n"); return dex_future_first (dex_timeout_new_seconds (1), #ifdef G_OS_UNIX dex_unix_signal_new (SIGINT), #endif NULL); } int main (int argc, char *argv[]) { GMainLoop *main_loop; DexFuture *loop; dex_init (); main_loop = g_main_loop_new (NULL, FALSE); loop = dex_future_finally_loop (dex_future_first (dex_timeout_new_seconds (1), #ifdef G_OS_UNIX dex_unix_signal_new (SIGINT), #endif NULL), infinite_loop_cb, g_main_loop_ref (main_loop), (GDestroyNotify)g_main_loop_unref); g_main_loop_run (main_loop); dex_unref (loop); g_main_loop_unref (main_loop); return 0; } 07070100000017000081A40000000000000000000000016712D33C000002BF000000000000000000000000000000000000002200000000libdex-0.8.1/examples/meson.buildlibgio_unix_dep = dependency('gio-unix-2.0', required: false, disabler: true) libsoup_dep = dependency('libsoup-3.0', required: false, disabler: true) examples = { 'cat': {'dependencies': libgio_unix_dep}, 'cat-aio': {}, 'cp': {}, 'echo-bench': {}, 'host': {}, 'httpd': {'dependencies': libsoup_dep}, 'infinite-loop': {}, 'tcp-echo': {}, 'wget': {'dependencies': libsoup_dep}, } foreach example, params: examples example_exe = executable(example, '@0@.c'.format(example), c_args: deprecated_c_args, dependencies: [libdex_static_dep, params.get('dependencies', [])], install: false, ) endforeach 07070100000018000081A40000000000000000000000016712D33C0000103D000000000000000000000000000000000000002100000000libdex-0.8.1/examples/tcp-echo.c/* tcp-echo.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> static DexScheduler *thread_pool; static DexFuture * socket_connection_fiber (gpointer user_data) { GSocketConnection *connection = user_data; GOutputStream *output; GInputStream *input; GError *error = NULL; guint8 buffer[1024]; g_assert (G_IS_SOCKET_CONNECTION (connection)); input = g_io_stream_get_input_stream (G_IO_STREAM (connection)); output = g_io_stream_get_output_stream (G_IO_STREAM (connection)); for (;;) { gssize n_read; gssize to_write; gssize n_written; n_read = dex_await_int64 (dex_input_stream_read (input, buffer, sizeof buffer, G_PRIORITY_DEFAULT), &error); if (n_read == 0 || error != NULL) break; for (to_write = n_read; to_write > 0; to_write -= n_written) { n_written = dex_await_int64 (dex_output_stream_write (output, &buffer[n_read-to_write], to_write, G_PRIORITY_HIGH), &error); if (n_written == 0 || error != NULL) break; } } return error ? dex_future_new_for_error (error) : NULL; } static DexFuture * socket_listener_fiber (gpointer user_data) { GSocketListener *socket_listener = user_data; g_assert (G_IS_SOCKET_LISTENER (socket_listener)); for (;;) { g_autoptr(GSocketConnection) connection = NULL; g_autoptr(DexFuture) fiber = NULL; g_autoptr(GError) error = NULL; /* Accept an incoming connection */ if (!(connection = dex_await_object (dex_socket_listener_accept (socket_listener), &error))) return dex_future_new_for_error (g_steal_pointer (&error)); /* Spawn a fiber to handle connection on thread pool */ fiber = dex_scheduler_spawn (thread_pool, 0, socket_connection_fiber, g_steal_pointer (&connection), g_object_unref); } return NULL; } static DexFuture * quit_cb (DexFuture *completed, gpointer user_data) { GMainLoop *main_loop = user_data; g_main_loop_quit (main_loop); return NULL; } int main (int argc, char *argv[]) { g_autoptr(GSocketListener) socket_listener = NULL; g_autoptr(GMainLoop) main_loop = NULL; g_autoptr(DexFuture) future = NULL; g_autoptr(GError) error = NULL; guint port = 8080; dex_init (); main_loop = g_main_loop_new (NULL, FALSE); thread_pool = dex_thread_pool_scheduler_new (); socket_listener = g_socket_listener_new (); /* Try to listen on configured port, or bail */ if (!g_socket_listener_add_inet_port (socket_listener, port, NULL, &error)) g_error ("Failed to listen on port %u: %s", port, error->message); g_print ("Listening on 0.0.0.0:%u\n", port); /* Spawn a fiber on current thread for socket loop */ future = dex_scheduler_spawn (NULL, 0, socket_listener_fiber, g_object_ref (socket_listener), g_object_unref); /* When it completes, call quit_cb to quit main loop */ future = dex_future_finally (future, quit_cb, g_main_loop_ref (main_loop), (GDestroyNotify) g_main_loop_unref); /* block on our main loop until quit_cb runs */ g_main_loop_run (main_loop); return 0; } 07070100000019000081A40000000000000000000000016712D33C00001B14000000000000000000000000000000000000001D00000000libdex-0.8.1/examples/wget.c/* wget.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <unistd.h> #include <libsoup/soup.h> #include <libdex.h> #define PRIO G_PRIORITY_DEFAULT static DexFuture *session_send (SoupSession *session, SoupMessage *message, int priority); static char *output_document; static char *url; static GMainLoop *main_loop; static int exit_code; static const GOptionEntry entries[] = { { "output-document", 'o', 0, G_OPTION_ARG_FILENAME, &output_document, "write documents to FILE", "FILE" }, { NULL } }; static DexFuture * wget (gpointer user_data) { g_autoptr(GOutputStream) output = NULL; g_autoptr(GInputStream) input = NULL; g_autoptr(SoupMessage) message = NULL; g_autoptr(SoupSession) session = NULL; g_autoptr(GError) error = NULL; g_autoptr(GFile) file = NULL; gssize len; /* Create SoupSession used to manage connections */ session = soup_session_new_with_options ("user-agent", "libdex-wget", "accept-language-auto", TRUE, "timeout", 15, NULL); /* Create SoupMessage to submit request and get response */ if (!(message = soup_message_new ("GET", url))) return dex_future_new_reject (G_URI_ERROR, G_URI_ERROR_FAILED, "Failed to parse url \"%s\"", url); /* Suspend until we get a response or error */ if (!(input = dex_await_object (session_send (session, message, PRIO), &error))) return dex_future_new_for_error (g_steal_pointer (&error)); /* If output_document is not specified, use the URI to guess a name */ if (output_document == NULL) { GUri *uri = soup_message_get_uri (message); const char *path = g_uri_get_path (uri); /* Try to get a reasonable name for the file */ output_document = path ? g_path_get_basename (path) : g_strdup ("output"); /* Avoid https://example.com/ having a filename of "/" */ if (output_document == NULL || output_document[0] == '/') { g_free (output_document); output_document = g_strdup ("index.html"); } } /* Suspend while the file at @output_document is created or replaced */ file = g_file_new_for_path (output_document); if (!(output = dex_await_object (dex_file_replace (file, NULL, TRUE, G_FILE_CREATE_REPLACE_DESTINATION, PRIO), &error))) return dex_future_new_for_error (g_steal_pointer (&error)); /* Suspend while the input stream is spliced to our output stream */ len = dex_await_int64 (dex_output_stream_splice (output, input, 0, PRIO), &error); if (error != NULL) return dex_future_new_for_error (g_steal_pointer (&error)); /* Suspend while we wait for @output to close/sync */ if (!dex_await_boolean (dex_output_stream_close (output, PRIO), &error)) return dex_future_new_for_error (g_steal_pointer (&error)); /* Return the length copied back */ return dex_future_new_for_int64 (len); } static DexFuture * fiber_completed (DexFuture *completed, gpointer value) { GError *error = NULL; gssize len; /* We can "await" here because we know @completed is a resolved * or rejected future. Otherwise, dex_await() only works on a fiber * to be able to suspend execution until the result is ready. */ len = dex_await_int64 (dex_ref (completed), &error); /* Give the user some information on success/failure */ if (error != NULL) g_printerr ("error: %s\n", error->message); else g_printerr ("wrote %"G_GSSIZE_FORMAT" bytes to \"%s\".\n", len, output_document); /* Set exit code for program and exit main loop */ exit_code = error != NULL ? EXIT_FAILURE : EXIT_SUCCESS; g_main_loop_quit (main_loop); return NULL; } int main (int argc, char *argv[]) { GOptionContext *context; DexFuture *future; GError *error = NULL; dex_init (); context = g_option_context_new ("- a non-interactive network retriever"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("%s: %s\n", argv[0], error->message); return EXIT_FAILURE; } if (argc != 2) { g_printerr ("usage: %s [OPTIONS...] URL\n", argv[0]); return EXIT_FAILURE; } url = g_strdup (argv[1]); main_loop = g_main_loop_new (NULL, FALSE); /* Spawn a fiber */ future = dex_scheduler_spawn (NULL, 0, wget, NULL, NULL); /* Handle resolve/reject of future and exit */ future = dex_future_finally (future, fiber_completed, NULL, NULL); g_main_loop_run (main_loop); dex_unref (future); g_option_context_free (context); g_main_loop_unref (main_loop); g_free (output_document); g_free (url); return exit_code; } /* This is a GAsyncResult wrapper which converts it into a DexFuture. * DexAsyncPair can be used for simple pairs currently, but lacks support * for additional parameters. I hope to add that in the future though. */ static void session_send_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GInputStream *stream; GError *error = NULL; stream = soup_session_send_finish (SOUP_SESSION (object), result, &error); if (error == NULL) dex_async_pair_return_object (async_pair, stream); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } static DexFuture * session_send (SoupSession *session, SoupMessage *message, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (SOUP_IS_SESSION (session), NULL); g_return_val_if_fail (SOUP_IS_MESSAGE (message), NULL); async_pair = (DexAsyncPair *)g_type_create_instance (DEX_TYPE_ASYNC_PAIR); soup_session_send_async (session, message, priority, dex_async_pair_get_cancellable (async_pair), session_send_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } 0707010000001A000081A40000000000000000000000016712D33C00000383000000000000000000000000000000000000001900000000libdex-0.8.1/libdex.doap<?xml version="1.0" encoding="UTF-8"?> <Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:gnome="http://api.gnome.org/doap-extensions#" xmlns="http://usefulinc.com/ns/doap#"> <name>libdex</name> <shortname>libdex</shortname> <shortdesc>Future-based programming for GLib-based applications and libraries</shortdesc> <homepage rdf:resource="https://gitlab.gnome.org/GNOME/libdex" /> <license rdf:resource="https://spdx.org/licenses/LGPL-2.1-or-later.html" /> <programming-language>C</programming-language> <maintainer> <foaf:Person> <foaf:name>Christian Hergert</foaf:name> <foaf:mbox rdf:resource="mailto:chergert@gnome.org" /> <gnome:userid>chergert</gnome:userid> </foaf:Person> </maintainer> </Project> 0707010000001B000081A40000000000000000000000016712D33C00001779000000000000000000000000000000000000001900000000libdex-0.8.1/meson.buildproject('libdex', 'c', version: '0.8.1', meson_version: '>= 0.62.0', default_options: [ 'warning_level=2', 'werror=false', 'c_std=gnu11', ], ) api_version = '1' cc = meson.get_compiler('c') gnome = import('gnome') pkg = import('pkgconfig') config_h = configuration_data() config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) prefix = get_option('prefix') datadir = join_paths(prefix, get_option('datadir')) libdir = join_paths(prefix, get_option('libdir')) girdir = join_paths(datadir, 'gir-1.0') vapidir = join_paths(datadir, 'vala', 'vapi') typelibdir = join_paths(libdir, 'girepository-1.0') package_name = meson.project_name() package_string = '@0@-@1@'.format(package_name, api_version) glib_req_version = '2.68' glib_req = '>= @0@'.format(glib_req_version) glib_dep = dependency('gio-2.0', version: glib_req) if host_machine.system() == 'linux' and not get_option('liburing').disabled() liburing_req_version = '0.7' liburing_req = '>= @0@'.format(liburing_req_version) liburing_dep = dependency('liburing', version: liburing_req, required: get_option('liburing').enabled()) if liburing_dep.found() config_h.set10('HAVE_LIBURING', true) endif else liburing_dep = disabler() endif if get_option('sysprof') libsysprof_capture_dep = dependency('sysprof-capture-4') config_h.set10('HAVE_SYSPROF', true) endif check_headers = [ 'ucontext.h', ] foreach h : check_headers if cc.has_header(h) config_h.set('HAVE_' + h.underscorify().to_upper(), 1) endif endforeach if host_machine.system() == 'darwin' # known alignment for darwin where we're using helpers if host_machine.cpu_family() == 'aarch64' config_h.set('ALIGN_OF_UCONTEXT', 16) else config_h.set('ALIGN_OF_UCONTEXT', 8) endif elif host_machine.system() == 'windows' # Unset else # Check alignment of ucontext_t config_h.set('ALIGN_OF_UCONTEXT', cc.alignment('ucontext_t', prefix: '#include <ucontext.h>')) endif project_c_args = [] test_c_args = [ '-Watomic-alignment', '-Wcast-align', '-Wdeclaration-after-statement', '-Werror=address', '-Werror=array-bounds', '-Werror=empty-body', '-Werror=implicit', '-Werror=implicit-function-declaration', '-Werror=incompatible-pointer-types', '-Werror=init-self', '-Werror=int-conversion', '-Werror=int-to-pointer-cast', '-Werror=main', '-Werror=misleading-indentation', '-Werror=missing-braces', '-Werror=missing-include-dirs', '-Werror=nonnull', '-Werror=overflow', '-Werror=parenthesis', '-Werror=pointer-arith', '-Werror=pointer-to-int-cast', '-Werror=redundant-decls', '-Werror=return-type', '-Werror=sequence-point', '-Werror=shadow', '-Werror=strict-prototypes', '-Werror=trigraphs', '-Werror=undef', '-Werror=write-strings', '-Wformat-nonliteral', '-Wignored-qualifiers', '-Wimplicit-function-declaration', '-Wlogical-op', '-Wmissing-declarations', '-Wmissing-format-attribute', '-Wmissing-include-dirs', '-Wmissing-noreturn', '-Wnested-externs', '-Wno-cast-function-type', '-Wno-dangling-pointer', '-Wno-missing-field-initializers', '-Wno-sign-compare', '-Wno-unused-parameter', '-Wold-style-definition', '-Wpointer-arith', '-Wredundant-decls', '-Wstrict-prototypes', '-Wswitch-default', '-Wswitch-enum', '-Wtrampolines', '-Wundef', '-Wuninitialized', '-Wunused', '-fstrict-flex-arrays=3', '-fno-strict-aliasing', ['-Werror=format-security', '-Werror=format=2'], '-FImsvc_recommended_pragmas.h', ] if get_option('buildtype') != 'plain' and get_option('stack-protector') if host_machine.system() != 'windows' test_c_args += '-fstack-protector-strong' endif endif foreach arg: test_c_args if cc.has_multi_arguments(arg) project_c_args += arg endif endforeach add_project_arguments(project_c_args, language: 'c') # Detect and set symbol visibility if get_option('default_library') != 'static' if host_machine.system() == 'windows' config_h.set('DLL_EXPORT', true) if cc.get_id() == 'msvc' config_h.set('_DEX_EXTERN', '__declspec(dllexport) extern') elif cc.has_argument('-fvisibility=hidden') config_h.set('_DEX_EXTERN', '__attribute__((visibility("default"))) __declspec(dllexport) extern') endif elif cc.has_argument('-fvisibility=hidden') config_h.set('_DEX_EXTERN', '__attribute__((visibility("default"))) extern') endif endif release_args = [] global_link_args = [] test_link_args = [ '-Wl,-z,relro', '-Wl,-z,now', '-Wl,-z,noexecstack', ] if not get_option('buildtype').startswith('debug') add_global_arguments('-DG_DISABLE_CAST_CHECKS', language: 'c') release_args += [ '-DG_DISABLE_ASSERT' ] test_link_args += [ '-Wl,-Bsymbolic', '-fno-plt', ] endif foreach link_arg: test_link_args if cc.has_link_argument(link_arg) global_link_args += link_arg endif endforeach add_project_link_arguments(global_link_args, language: 'c') glib_major_version = glib_req_version.split('.')[0].to_int() glib_minor_version = glib_req_version.split('.')[1].to_int() if glib_minor_version % 2 == 1 glib_minor_version = glib_minor_version + 1 endif deprecated_c_args = [ '-DG_DISABLE_DEPRECATED', '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_@0@_@1@'.format(glib_major_version, glib_minor_version), ] add_project_arguments('-I' + meson.project_build_root(), language: 'c') functions = [ 'posix_fadvise', 'madvise', 'mprotect', ] if not get_option('eventfd').disabled() functions += ['eventfd'] endif foreach f : functions if cc.has_function(f) define = 'HAVE_' + f.underscorify().to_upper() config_h.set10(define, true) endif endforeach if get_option('eventfd').enabled() and config_h.get('HAVE_EVENTFD') == 0 error('eventfd function is required for -Deventfd=enabled') endif configure_file(output: 'config.h', configuration: config_h) subdir('src') if get_option('tests') subdir('testsuite') endif if get_option('examples') subdir('examples') endif if get_option('docs') subdir('docs') endif 0707010000001C000081A40000000000000000000000016712D33C00000428000000000000000000000000000000000000001F00000000libdex-0.8.1/meson_options.txtoption('docs', type: 'boolean', value: false, description: 'Build reference manual (requires gi-doc and gobject-introspection)') option('examples', type: 'boolean', value: true, description: 'Build example programs') option('stack-protector', type: 'boolean', value: true, description: 'Enable stack-protector') option('vapi', type: 'boolean', value: true, description: 'Generate vapi data (requires vapigen)') option('introspection', type: 'feature', value: 'enabled', description: 'Generate gir data (requires gobject-introspection)') option('sysprof', type: 'boolean', value: false, description: 'Provide anscillary profiling information when run under Sysprof') option('tests', type: 'boolean', value: true, description: 'Build and enable tests') option('liburing', type: 'feature', value: 'auto', description: 'Allow use of liburing (io_uring)') option('eventfd', type: 'feature', value: 'auto', description: 'Allow use of eventfd') 0707010000001D000041ED0000000000000000000000026712D33C00000000000000000000000000000000000000000000001100000000libdex-0.8.1/src0707010000001E000081A40000000000000000000000016712D33C000011EE000000000000000000000000000000000000002000000000libdex-0.8.1/src/386-ucontext.h#define setcontext(u) setmcontext(&(u)->uc_mcontext) #define getcontext(u) getmcontext(&(u)->uc_mcontext) typedef struct mcontext mcontext_t; typedef struct ucontext ucontext_t; extern int swapcontext(ucontext_t*, const ucontext_t*); extern void makecontext(ucontext_t*, void(*)(), int, ...); extern int getmcontext(mcontext_t*); extern void setmcontext(const mcontext_t*); /*- * Copyright (c) 1999 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $ */ /* #include <machine/ucontext.h> */ /*- * Copyright (c) 1999 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ */ struct mcontext { /* * The first 20 fields must match the definition of * sigcontext. So that we can support sigcontext * and ucontext_t at the same time. */ int mc_onstack; /* sigcontext compat. */ int mc_gs; int mc_fs; int mc_es; int mc_ds; int mc_edi; int mc_esi; int mc_ebp; int mc_isp; int mc_ebx; int mc_edx; int mc_ecx; int mc_eax; int mc_trapno; int mc_err; int mc_eip; int mc_cs; int mc_eflags; int mc_esp; /* machine state */ int mc_ss; int mc_fpregs[28]; /* env87 + fpacc87 + u_long */ int __spare__[17]; }; struct ucontext { /* * Keep the order of the first two fields. Also, * keep them the first two fields in the structure. * This way we can have a union with struct * sigcontext and ucontext_t. This allows us to * support them both at the same time. * note: the union is not defined, though. */ sigset_t uc_sigmask; mcontext_t uc_mcontext; struct __ucontext *uc_link; stack_t uc_stack; int __spare__[8]; }; 0707010000001F000081A40000000000000000000000016712D33C000013FB000000000000000000000000000000000000002200000000libdex-0.8.1/src/amd64-ucontext.h#define setcontext(u) setmcontext(&(u)->uc_mcontext) #define getcontext(u) getmcontext(&(u)->uc_mcontext) typedef struct mcontext mcontext_t; typedef struct ucontext ucontext_t; extern int swapcontext(ucontext_t*, const ucontext_t*); extern void makecontext(ucontext_t*, void(*)(void), int, ...); extern int getmcontext(mcontext_t*); extern void setmcontext(const mcontext_t*); /*- * Copyright (c) 1999 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $ */ /* #include <machine/ucontext.h> */ /*- * Copyright (c) 1999 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ */ struct mcontext { /* * The first 20 fields must match the definition of * sigcontext. So that we can support sigcontext * and ucontext_t at the same time. */ long mc_onstack; /* sigcontext compat. */ long mc_rdi; /* machine state (struct trapframe) */ long mc_rsi; long mc_rdx; long mc_rcx; long mc_r8; long mc_r9; long mc_rax; long mc_rbx; long mc_rbp; long mc_r10; long mc_r11; long mc_r12; long mc_r13; long mc_r14; long mc_r15; long mc_trapno; long mc_addr; long mc_flags; long mc_err; long mc_rip; long mc_cs; long mc_rflags; long mc_rsp; long mc_ss; long mc_len; /* sizeof(mcontext_t) */ #define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */ #define _MC_FPFMT_XMM 0x10002 long mc_fpformat; #define _MC_FPOWNED_NONE 0x20000 /* FP state not used */ #define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */ #define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */ long mc_ownedfp; /* * See <machine/fpu.h> for the internals of mc_fpstate[]. */ long mc_fpstate[64]; long mc_spare[8]; }; struct ucontext { /* * Keep the order of the first two fields. Also, * keep them the first two fields in the structure. * This way we can have a union with struct * sigcontext and ucontext_t. This allows us to * support them both at the same time. * note: the union is not defined, though. */ sigset_t uc_sigmask; mcontext_t uc_mcontext; struct __ucontext *uc_link; stack_t uc_stack; int __spare__[8]; }; 07070100000020000081A40000000000000000000000016712D33C000012F1000000000000000000000000000000000000001700000000libdex-0.8.1/src/asm.S/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ #include "config.h" #if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5 # define NEEDX86CONTEXT 1 # define SET setmcontext # define GET getmcontext #endif #if defined(__OpenBSD__) && defined(__i386__) # define NEEDX86CONTEXT 1 # define SET setmcontext # define GET getmcontext #endif #ifdef __APPLE__ /* We no longer support asm.S task switching on macOS */ # error "asm.S is not supported on Apple operating systems #endif #if defined(__linux__) && defined(__mips__) && !defined(HAVE_UCONTEXT_H) # define NEEDMIPSCONTEXT 1 # define SET setmcontext # define GET getmcontext #endif #ifdef NEEDX86CONTEXT .globl SET SET: movl 4(%esp), %eax movl 8(%eax), %fs movl 12(%eax), %es movl 16(%eax), %ds movl 76(%eax), %ss movl 20(%eax), %edi movl 24(%eax), %esi movl 28(%eax), %ebp movl 36(%eax), %ebx movl 40(%eax), %edx movl 44(%eax), %ecx movl 72(%eax), %esp pushl 60(%eax) /* new %eip */ movl 48(%eax), %eax ret .globl GET GET: movl 4(%esp), %eax movl %fs, 8(%eax) movl %es, 12(%eax) movl %ds, 16(%eax) movl %ss, 76(%eax) movl %edi, 20(%eax) movl %esi, 24(%eax) movl %ebp, 28(%eax) movl %ebx, 36(%eax) movl %edx, 40(%eax) movl %ecx, 44(%eax) movl $1, 48(%eax) /* %eax */ movl (%esp), %ecx /* %eip */ movl %ecx, 60(%eax) leal 4(%esp), %ecx /* %esp */ movl %ecx, 72(%eax) movl 44(%eax), %ecx /* restore %ecx */ movl $0, %eax ret #endif #ifdef NEEDAMD64CONTEXT .globl SET SET: movq 16(%rdi), %rsi movq 24(%rdi), %rdx movq 32(%rdi), %rcx movq 40(%rdi), %r8 movq 48(%rdi), %r9 movq 56(%rdi), %rax movq 64(%rdi), %rbx movq 72(%rdi), %rbp movq 80(%rdi), %r10 movq 88(%rdi), %r11 movq 96(%rdi), %r12 movq 104(%rdi), %r13 movq 112(%rdi), %r14 movq 120(%rdi), %r15 movq 184(%rdi), %rsp pushq 160(%rdi) /* new %eip */ movq 8(%rdi), %rdi ret .globl GET GET: movq %rdi, 8(%rdi) movq %rsi, 16(%rdi) movq %rdx, 24(%rdi) movq %rcx, 32(%rdi) movq %r8, 40(%rdi) movq %r9, 48(%rdi) movq $1, 56(%rdi) /* %rax */ movq %rbx, 64(%rdi) movq %rbp, 72(%rdi) movq %r10, 80(%rdi) movq %r11, 88(%rdi) movq %r12, 96(%rdi) movq %r13, 104(%rdi) movq %r14, 112(%rdi) movq %r15, 120(%rdi) movq (%rsp), %rcx /* %rip */ movq %rcx, 160(%rdi) leaq 8(%rsp), %rcx /* %rsp */ movq %rcx, 184(%rdi) movq 32(%rdi), %rcx /* restore %rcx */ movq $0, %rax ret #endif #ifdef NEEDPOWERCONTEXT /* get FPR and VR use flags with sc 0x7FF3 */ /* get vsave with mfspr reg, 256 */ .text .align 2 .globl GET GET: /* xxx: instruction scheduling */ mflr r0 mfcr r5 mfctr r6 mfxer r7 stw r0, 0*4(r3) stw r5, 1*4(r3) stw r6, 2*4(r3) stw r7, 3*4(r3) stw r1, 4*4(r3) stw r2, 5*4(r3) li r5, 1 /* return value for setmcontext */ stw r5, 6*4(r3) stw r13, (0+7)*4(r3) /* callee-save GPRs */ stw r14, (1+7)*4(r3) /* xxx: block move */ stw r15, (2+7)*4(r3) stw r16, (3+7)*4(r3) stw r17, (4+7)*4(r3) stw r18, (5+7)*4(r3) stw r19, (6+7)*4(r3) stw r20, (7+7)*4(r3) stw r21, (8+7)*4(r3) stw r22, (9+7)*4(r3) stw r23, (10+7)*4(r3) stw r24, (11+7)*4(r3) stw r25, (12+7)*4(r3) stw r26, (13+7)*4(r3) stw r27, (14+7)*4(r3) stw r28, (15+7)*4(r3) stw r29, (16+7)*4(r3) stw r30, (17+7)*4(r3) stw r31, (18+7)*4(r3) li r3, 0 /* return */ blr .globl SET SET: lwz r13, (0+7)*4(r3) /* callee-save GPRs */ lwz r14, (1+7)*4(r3) /* xxx: block move */ lwz r15, (2+7)*4(r3) lwz r16, (3+7)*4(r3) lwz r17, (4+7)*4(r3) lwz r18, (5+7)*4(r3) lwz r19, (6+7)*4(r3) lwz r20, (7+7)*4(r3) lwz r21, (8+7)*4(r3) lwz r22, (9+7)*4(r3) lwz r23, (10+7)*4(r3) lwz r24, (11+7)*4(r3) lwz r25, (12+7)*4(r3) lwz r26, (13+7)*4(r3) lwz r27, (14+7)*4(r3) lwz r28, (15+7)*4(r3) lwz r29, (16+7)*4(r3) lwz r30, (17+7)*4(r3) lwz r31, (18+7)*4(r3) lwz r1, 4*4(r3) lwz r2, 5*4(r3) lwz r0, 0*4(r3) mtlr r0 lwz r0, 1*4(r3) mtcr r0 /* mtcrf 0xFF, r0 */ lwz r0, 2*4(r3) mtctr r0 lwz r0, 3*4(r3) mtxer r0 lwz r3, 6*4(r3) blr #endif #ifdef NEEDMIPSCONTEXT .globl GET GET: sw $4, 24($4) sw $5, 28($4) sw $6, 32($4) sw $7, 36($4) sw $16, 72($4) sw $17, 76($4) sw $18, 80($4) sw $19, 84($4) sw $20, 88($4) sw $21, 92($4) sw $22, 96($4) sw $23, 100($4) sw $28, 120($4) /* gp */ sw $29, 124($4) /* sp */ sw $30, 128($4) /* fp */ sw $31, 132($4) /* ra */ xor $2, $2, $2 j $31 nop .globl SET SET: lw $16, 72($4) lw $17, 76($4) lw $18, 80($4) lw $19, 84($4) lw $20, 88($4) lw $21, 92($4) lw $22, 96($4) lw $23, 100($4) lw $28, 120($4) /* gp */ lw $29, 124($4) /* sp */ lw $30, 128($4) /* fp */ /* * If we set $31 directly and j $31, * we would loose the outer return address. * Use a temporary register, then. */ lw $8, 132($4) /* ra */ /* bug: not setting the pc causes a bus error */ lw $25, 132($4) /* pc */ lw $5, 28($4) lw $6, 32($4) lw $7, 36($4) lw $4, 24($4) j $8 nop #endif 07070100000021000081A40000000000000000000000016712D33C00000E33000000000000000000000000000000000000002B00000000libdex-0.8.1/src/dex-aio-backend-private.h/* dex-aio-backend-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-object-private.h" #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_AIO_BACKEND (dex_aio_backend_get_type()) #define DEX_AIO_BACKEND(object) (G_TYPE_CHECK_INSTANCE_CAST(object, DEX_TYPE_AIO_BACKEND, DexAioBackend)) #define DEX_IS_AIO_BACKEND(object) (G_TYPE_CHECK_INSTANCE_TYPE(object, DEX_TYPE_AIO_BACKEND)) #define DEX_AIO_BACKEND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, DEX_TYPE_AIO_BACKEND, DexAioBackendClass)) #define DEX_AIO_BACKEND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, DEX_TYPE_AIO_BACKEND, DexAioBackendClass)) typedef struct _DexAioBackend DexAioBackend; typedef struct _DexAioBackendClass DexAioBackendClass; typedef struct _DexAioContext DexAioContext; struct _DexAioBackend { DexObject parent_instance; }; struct _DexAioBackendClass { DexObjectClass parent_class; DexAioContext *(*create_context) (DexAioBackend *aio_backend); DexFuture *(*read) (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gpointer buffer, gsize count, goffset offset); DexFuture *(*write) (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gconstpointer buffer, gsize count, goffset offset); }; struct _DexAioContext { GSource parent_source; DexAioBackend *aio_backend; /*< private >*/ }; GType dex_aio_backend_get_type (void) G_GNUC_CONST; DexAioBackend *dex_aio_backend_get_default (void); DexAioContext *dex_aio_backend_create_context (DexAioBackend *aio_backend); DexFuture *dex_aio_backend_read (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gpointer buffer, gsize count, goffset offset); DexFuture *dex_aio_backend_write (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gconstpointer buffer, gsize count, goffset offset); G_END_DECLS 07070100000022000081A40000000000000000000000016712D33C00000B95000000000000000000000000000000000000002300000000libdex-0.8.1/src/dex-aio-backend.c/* dex-aio-backend.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-aio-backend-private.h" #ifdef HAVE_LIBURING # include "dex-uring-aio-backend-private.h" #endif #include "dex-posix-aio-backend-private.h" DEX_DEFINE_ABSTRACT_TYPE (DexAioBackend, dex_aio_backend, DEX_TYPE_OBJECT) static void dex_aio_backend_class_init (DexAioBackendClass *aio_backend_class) { } static void dex_aio_backend_init (DexAioBackend *aio_backend) { } DexAioContext * dex_aio_backend_create_context (DexAioBackend *aio_backend) { g_return_val_if_fail (DEX_IS_AIO_BACKEND (aio_backend), NULL); return DEX_AIO_BACKEND_GET_CLASS (aio_backend)->create_context (aio_backend); } DexFuture * dex_aio_backend_read (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gpointer buffer, gsize count, goffset offset) { g_return_val_if_fail (DEX_IS_AIO_BACKEND (aio_backend), NULL); g_return_val_if_fail (aio_context != NULL, NULL); return DEX_AIO_BACKEND_GET_CLASS (aio_backend)->read (aio_backend, aio_context, fd, buffer, count, offset); } DexFuture * dex_aio_backend_write (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gconstpointer buffer, gsize count, goffset offset) { g_return_val_if_fail (DEX_IS_AIO_BACKEND (aio_backend), NULL); g_return_val_if_fail (aio_context != NULL, NULL); return DEX_AIO_BACKEND_GET_CLASS (aio_backend)->write (aio_backend, aio_context, fd, buffer, count, offset); } DexAioBackend * dex_aio_backend_get_default (void) { static DexAioBackend *instance; if (g_once_init_enter (&instance)) { DexAioBackend *backend = NULL; #if defined(HAVE_LIBURING) backend = dex_uring_aio_backend_new (); #endif if (backend == NULL) backend = dex_posix_aio_backend_new (); g_debug ("Using AIO backend %s", DEX_OBJECT_TYPE_NAME (backend)); g_once_init_leave (&instance, backend); } return instance; } 07070100000023000081A40000000000000000000000016712D33C00000A0C000000000000000000000000000000000000001B00000000libdex-0.8.1/src/dex-aio.c/* dex-aio.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-aio.h" #include "dex-aio-backend-private.h" #include "dex-scheduler-private.h" #include "dex-thread-storage-private.h" static DexAioContext * dex_aio_context_current (void) { DexThreadStorage *storage = dex_thread_storage_get (); if (storage->aio_context) return storage->aio_context; if (storage->scheduler) return dex_scheduler_get_aio_context (storage->scheduler); g_return_val_if_reached (NULL); } /** * dex_aio_read: * @buffer: (array length=count) (element-type guint8) (out caller-allocates) * @count: (in) * * An asynchronous `pread()` wrapper. * * Returns: (transfer full): a future that will resolve when the * read completes or rejects with error. */ DexFuture * dex_aio_read (DexAioContext *aio_context, int fd, gpointer buffer, gsize count, goffset offset) { if (aio_context == NULL) aio_context = dex_aio_context_current (); return dex_aio_backend_read (aio_context->aio_backend, aio_context, fd, buffer, count, offset); } /** * dex_aio_write: * @buffer: (array length=count) (element-type guint8) * * An asynchronous `pwrite()` wrapper. * * Returns: (transfer full): a future that will resolve when the * write completes or rejects with error. */ DexFuture * dex_aio_write (DexAioContext *aio_context, int fd, gconstpointer buffer, gsize count, goffset offset) { if (aio_context == NULL) aio_context = dex_aio_context_current (); return dex_aio_backend_write (aio_context->aio_backend, aio_context, fd, buffer, count, offset); } 07070100000024000081A40000000000000000000000016712D33C000005D7000000000000000000000000000000000000001B00000000libdex-0.8.1/src/dex-aio.h/* dex-aio.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS typedef struct _DexAioContext DexAioContext; DEX_AVAILABLE_IN_ALL DexFuture *dex_aio_read (DexAioContext *aio_context, int fd, gpointer buffer, gsize count, goffset offset) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_aio_write (DexAioContext *aio_context, int fd, gconstpointer buffer, gsize count, goffset offset) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS 07070100000025000081A40000000000000000000000016712D33C000004C3000000000000000000000000000000000000002A00000000libdex-0.8.1/src/dex-async-pair-private.h/* * dex-async-pair-private.h * * Copyright 2022-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gio/gio.h> #include "dex-async-pair.h" #include "dex-future-private.h" G_BEGIN_DECLS typedef struct _DexAsyncPair { DexFuture parent_instance; gpointer instance; GCancellable *cancellable; DexAsyncPairInfo *info; guint cancel_on_discard : 1; } DexAsyncPair; typedef struct _DexAsyncPairClass { DexFutureClass parent_class; } DexAsyncPairClass; G_END_DECLS 07070100000026000081A40000000000000000000000016712D33C00002D21000000000000000000000000000000000000002200000000libdex-0.8.1/src/dex-async-pair.c/* * dex-async-pair.c * * Copyright 2022-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gio/gio.h> #include "dex-async-pair-private.h" #include "dex-error.h" DEX_DEFINE_FINAL_TYPE (DexAsyncPair, dex_async_pair, DEX_TYPE_FUTURE) #undef DEX_TYPE_ASYNC_PAIR #define DEX_TYPE_ASYNC_PAIR dex_async_pair_type static void dex_async_pair_discard (DexFuture *future) { DexAsyncPair *async_pair = DEX_ASYNC_PAIR (future); if (async_pair->cancel_on_discard) g_cancellable_cancel (async_pair->cancellable); } static void dex_async_pair_finalize (DexObject *object) { DexAsyncPair *async_pair = DEX_ASYNC_PAIR (object); g_clear_object (&async_pair->instance); g_clear_object (&async_pair->cancellable); g_clear_pointer (&async_pair->info, g_free); DEX_OBJECT_CLASS (dex_async_pair_parent_class)->finalize (object); } static void dex_async_pair_class_init (DexAsyncPairClass *async_pair_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (async_pair_class); DexFutureClass *future_class = DEX_FUTURE_CLASS (async_pair_class); object_class->finalize = dex_async_pair_finalize; future_class->discard = dex_async_pair_discard; } static void dex_async_pair_init (DexAsyncPair *async_pair) { async_pair->cancellable = g_cancellable_new (); async_pair->cancel_on_discard = TRUE; } static void dex_async_pair_ready_callback (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GValue value = G_VALUE_INIT; GError *error = NULL; GType gtype; g_assert (G_IS_OBJECT (object)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (DEX_IS_ASYNC_PAIR (async_pair)); if (g_cancellable_is_cancelled (async_pair->cancellable)) { error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Operation cancelled"); goto complete; } #define FINISH_AS(ap, TYPE) \ (((TYPE (*) (gpointer, GAsyncResult*, GError**))ap->info->finish) (ap->instance, result, &error)) gtype = async_pair->info->return_type; switch (gtype) { case G_TYPE_BOOLEAN: g_value_init (&value, G_TYPE_BOOLEAN); g_value_set_boolean (&value, FINISH_AS (async_pair, gboolean)); break; case G_TYPE_INT: g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, FINISH_AS (async_pair, int)); break; case G_TYPE_UINT: g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, FINISH_AS (async_pair, guint)); break; case G_TYPE_INT64: g_value_init (&value, G_TYPE_INT64); g_value_set_int64 (&value, FINISH_AS (async_pair, gint64)); break; case G_TYPE_UINT64: g_value_init (&value, G_TYPE_UINT64); g_value_set_uint64 (&value, FINISH_AS (async_pair, guint64)); break; case G_TYPE_LONG: g_value_init (&value, G_TYPE_LONG); g_value_set_long (&value, FINISH_AS (async_pair, glong)); break; case G_TYPE_ULONG: g_value_init (&value, G_TYPE_ULONG); g_value_set_ulong (&value, FINISH_AS (async_pair, gulong)); break; case G_TYPE_STRING: g_value_init (&value, G_TYPE_STRING); g_value_take_string (&value, FINISH_AS (async_pair, char *)); break; case G_TYPE_POINTER: g_value_init (&value, G_TYPE_POINTER); g_value_set_pointer (&value, FINISH_AS (async_pair, gpointer)); break; case G_TYPE_OBJECT: g_value_init (&value, G_TYPE_OBJECT); g_value_take_object (&value, FINISH_AS (async_pair, gpointer)); break; case G_TYPE_ENUM: case G_TYPE_FLAGS: default: if (gtype == G_TYPE_ENUM || g_type_is_a (gtype, G_TYPE_ENUM)) { g_value_init (&value, gtype); g_value_set_enum (&value, FINISH_AS (async_pair, guint)); } else if (gtype == G_TYPE_FLAGS || g_type_is_a (gtype, G_TYPE_FLAGS)) { g_value_init (&value, gtype); g_value_set_flags (&value, FINISH_AS (async_pair, guint)); } else if (g_type_is_a (gtype, G_TYPE_BOXED)) { g_value_init (&value, gtype); g_value_take_boxed (&value, FINISH_AS (async_pair, gpointer)); } else { error = g_error_new (DEX_ERROR, DEX_ERROR_TYPE_NOT_SUPPORTED, "Type '%s' is not currently supported by DexAsyncPair!", g_type_name (async_pair->info->return_type)); } break; } #undef FINISH_AS complete: if (error != NULL) dex_future_complete (DEX_FUTURE (async_pair), NULL, g_steal_pointer (&error)); else dex_future_complete (DEX_FUTURE (async_pair), &value, NULL); g_value_unset (&value); dex_clear (&async_pair); } DexFuture * dex_async_pair_new (gpointer instance, const DexAsyncPairInfo *info) { void (*async_func) (gpointer, GCancellable*, GAsyncReadyCallback, gpointer); DexAsyncPair *async_pair; g_return_val_if_fail (!instance || G_IS_OBJECT (instance), NULL); g_return_val_if_fail (info != NULL, NULL); async_func = info->async; async_pair = (DexAsyncPair *)dex_object_create_instance (DEX_TYPE_ASYNC_PAIR); async_pair->info = g_memdup2 (info, sizeof *info); g_set_object (&async_pair->instance, instance); async_func (async_pair->instance, async_pair->cancellable, dex_async_pair_ready_callback, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } /** * dex_async_pair_get_cancellable: * @async_pair: a #DexAsyncPair * * Gets the cancellable for the async pair. * * If the DexAsyncPair is discarded by its callers, then it will automatically * be cancelled using g_cancellable_cancel(). * * Returns: (transfer none): a #GCancellable */ GCancellable * dex_async_pair_get_cancellable (DexAsyncPair *async_pair) { g_return_val_if_fail (DEX_IS_ASYNC_PAIR (async_pair), NULL); return async_pair->cancellable; } /** * dex_async_pair_return_object: * @async_pair: a #DexAsyncPair * @instance: (type GObject) (transfer full): a #GObject * * Resolves @async_pair with a value of @instance. * * This function is meant to be used when manually wrapping * various #GAsyncReadyCallback based API. * * The ownership of @instance is taken when calling this function. */ void dex_async_pair_return_object (DexAsyncPair *async_pair, gpointer instance) { GValue value = G_VALUE_INIT; g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); g_return_if_fail (G_IS_OBJECT (instance)); g_value_init (&value, G_OBJECT_TYPE (instance)); g_value_take_object (&value, instance); dex_future_complete (DEX_FUTURE (async_pair), &value, NULL); g_value_unset (&value); } /** * dex_async_pair_return_error: * @async_pair: a #DexAsyncPair * @error: (transfer full): a #GError * * Rejects @async_pair with @error. * * This function is meant to be used when manually wrapping * various #GAsyncReadyCallback based API. * * The ownership of @error is taken when calling this function. */ void dex_async_pair_return_error (DexAsyncPair *async_pair, GError *error) { g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); g_return_if_fail (error != NULL); dex_future_complete (DEX_FUTURE (async_pair), NULL, error); } void dex_async_pair_return_int64 (DexAsyncPair *async_pair, gint64 value) { g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); dex_future_complete (DEX_FUTURE (async_pair), &(GValue) {G_TYPE_INT64, {{.v_int64 = value}}}, NULL); } void dex_async_pair_return_uint64 (DexAsyncPair *async_pair, guint64 value) { g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); dex_future_complete (DEX_FUTURE (async_pair), &(GValue) {G_TYPE_UINT64, {{.v_uint64 = value}}}, NULL); } void dex_async_pair_return_boolean (DexAsyncPair *async_pair, gboolean value) { g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); dex_future_complete (DEX_FUTURE (async_pair), &(GValue) {G_TYPE_BOOLEAN, {{.v_int = value}}}, NULL); } /** * dex_async_pair_return_string: * @async_pair: a #DexAsyncPair * @value: (transfer full) (nullable): a string or %NULL * * Resolves @async_pair with @value. */ void dex_async_pair_return_string (DexAsyncPair *async_pair, char *value) { GValue gvalue = G_VALUE_INIT; g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); g_value_init (&gvalue, G_TYPE_STRING); g_value_take_string (&gvalue, value); dex_future_complete (DEX_FUTURE (async_pair), &gvalue, NULL); g_value_unset (&gvalue); } /** * dex_async_pair_return_boxed: (skip) * @async_pair: a #DexAsyncPair * @boxed_type: a #GType of %G_TYPE_BOXED * @instance: (transfer full): the boxed value to store * * Resolves @async_pair with @instance. */ void dex_async_pair_return_boxed (DexAsyncPair *async_pair, GType boxed_type, gpointer instance) { GValue gvalue = G_VALUE_INIT; g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); g_return_if_fail (g_type_is_a (boxed_type, G_TYPE_BOXED)); g_value_init (&gvalue, boxed_type); g_value_take_boxed (&gvalue, instance); dex_future_complete (DEX_FUTURE (async_pair), &gvalue, NULL); g_value_unset (&gvalue); } /** * dex_async_pair_return_variant: * @async_pair: a #DexAsyncPair * @variant: (transfer full): the variant to resolve with * * Resolves @async_pair with @variant. */ void dex_async_pair_return_variant (DexAsyncPair *async_pair, GVariant *variant) { GValue gvalue = G_VALUE_INIT; g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); g_value_init (&gvalue, G_TYPE_VARIANT); g_value_take_variant (&gvalue, variant); dex_future_complete (DEX_FUTURE (async_pair), &gvalue, NULL); g_value_unset (&gvalue); } /** * dex_async_pair_set_cancel_on_discard: * @async_pair: a #DexAsyncPair * @cancel_on_discard: if the operation should cancel when the future is discarded * * Sets whether or not the future should cancel the async operation when * the future is discarded. This happens when no more futures are awaiting * the completion of this future. * * Since: 0.4 */ void dex_async_pair_set_cancel_on_discard (DexAsyncPair *async_pair, gboolean cancel_on_discard) { g_return_if_fail (DEX_IS_ASYNC_PAIR (async_pair)); dex_object_lock (async_pair); async_pair->cancel_on_discard = !!cancel_on_discard; dex_object_unlock (async_pair); } 07070100000027000081A40000000000000000000000016712D33C0000149E000000000000000000000000000000000000002200000000libdex-0.8.1/src/dex-async-pair.h/* * dex-async-pair.h * * Copyright 2022-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gio/gio.h> #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_ASYNC_PAIR (dex_async_pair_get_type()) #define DEX_ASYNC_PAIR(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_ASYNC_PAIR, DexAsyncPair)) #define DEX_IS_ASYNC_PAIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_ASYNC_PAIR)) typedef struct _DexAsyncPair DexAsyncPair; typedef struct _DexAsyncPairInfo { gpointer async; gpointer finish; GType return_type; /*< private >*/ gpointer _reserved[13]; } DexAsyncPairInfo; #ifndef __GI_SCANNER__ G_STATIC_ASSERT (sizeof (DexAsyncPairInfo) == (GLIB_SIZEOF_VOID_P * 16)); #endif #define DEX_ASYNC_PAIR_INFO(Async, Finish, ReturnType) \ (DexAsyncPairInfo) { \ .async = Async, \ .finish = Finish, \ .return_type = ReturnType, \ } #define DEX_ASYNC_PAIR_INFO_BOOLEAN(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_BOOLEAN) #define DEX_ASYNC_PAIR_INFO_INT(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_INT) #define DEX_ASYNC_PAIR_INFO_UINT(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_UINT) #define DEX_ASYNC_PAIR_INFO_INT64(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_INT64) #define DEX_ASYNC_PAIR_INFO_UINT64(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_UINT64) #define DEX_ASYNC_PAIR_INFO_LONG(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_LONG) #define DEX_ASYNC_PAIR_INFO_ULONG(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_ULONG) #define DEX_ASYNC_PAIR_INFO_STRING(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_STRING) #define DEX_ASYNC_PAIR_INFO_POINTER(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_POINTER) #define DEX_ASYNC_PAIR_INFO_OBJECT(Async, Finish) \ DEX_ASYNC_PAIR_INFO (Async, Finish, G_TYPE_OBJECT) #define DEX_ASYNC_PAIR_INFO_BOXED(Async, Finish, Type) \ DEX_ASYNC_PAIR_INFO (Async, Finish, Type) DEX_AVAILABLE_IN_ALL GType dex_async_pair_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexFuture *dex_async_pair_new (gpointer instance, const DexAsyncPairInfo *info) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL GCancellable *dex_async_pair_get_cancellable (DexAsyncPair *async_pair); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_object (DexAsyncPair *async_pair, gpointer instance); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_error (DexAsyncPair *async_pair, GError *error); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_int64 (DexAsyncPair *async_pair, gint64 value); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_uint64 (DexAsyncPair *async_pair, guint64 value); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_boolean (DexAsyncPair *async_pair, gboolean value); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_string (DexAsyncPair *async_pair, char *value); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_boxed (DexAsyncPair *async_pair, GType boxed_type, gpointer instance); DEX_AVAILABLE_IN_ALL void dex_async_pair_return_variant (DexAsyncPair *async_pair, GVariant *variant); DEX_AVAILABLE_IN_ALL void dex_async_pair_set_cancel_on_discard (DexAsyncPair *async_pair, gboolean cancel_on_discard); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexAsyncPair, dex_unref) G_END_DECLS 07070100000028000081A40000000000000000000000016712D33C00003116000000000000000000000000000000000000002400000000libdex-0.8.1/src/dex-async-result.c/* * dex-async-result.c * * Copyright 2022-2023 Christian Hergert <> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-async-result.h" #include "dex-cancellable.h" #include "dex-compat-private.h" #include "dex-error.h" /** * DexAsyncResult: * * `DexAsyncResult` is used to integrate a `DexFuture` with `GAsyncResult`. * * Use this class when you need to expose the traditional async/finish * behavior of `GAsyncResult`. */ struct _DexAsyncResult { GObject parent_instance; GMutex mutex; GMainContext *main_context; gpointer source_object; GCancellable *cancellable; GAsyncReadyCallback callback; gpointer user_data; gpointer tag; DexFuture *future; char *name; int priority; guint name_is_static : 1; guint await_once : 1; guint await_returned : 1; }; static gboolean dex_async_result_is_tagged (GAsyncResult *async_result, gpointer tag) { DexAsyncResult *self = DEX_ASYNC_RESULT (async_result); return self->tag == tag; } static gpointer dex_async_result_get_user_data (GAsyncResult *async_result) { DexAsyncResult *self = DEX_ASYNC_RESULT (async_result); return self->user_data; } static GObject * dex_async_result_get_source_object (GAsyncResult *async_result) { DexAsyncResult *self = DEX_ASYNC_RESULT (async_result); return self->source_object ? g_object_ref (self->source_object) : NULL; } static void async_result_iface_init (GAsyncResultIface *iface) { iface->get_user_data = dex_async_result_get_user_data; iface->get_source_object = dex_async_result_get_source_object; iface->is_tagged = dex_async_result_is_tagged; } G_DEFINE_FINAL_TYPE_WITH_CODE (DexAsyncResult, dex_async_result, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, async_result_iface_init)) DexAsyncResult * dex_async_result_new (gpointer source_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DexAsyncResult *self; g_return_val_if_fail (!source_object || G_IS_OBJECT (source_object), NULL); self = g_object_new (DEX_TYPE_ASYNC_RESULT, NULL); self->user_data = user_data; self->callback = callback; g_set_object (&self->source_object, source_object); g_set_object (&self->cancellable, cancellable); self->main_context = g_main_context_ref_thread_default (); return self; } static void dex_async_result_finalize (GObject *object) { DexAsyncResult *self = (DexAsyncResult *)object; dex_clear (&self->future); g_clear_object (&self->source_object); g_clear_object (&self->cancellable); g_clear_pointer (&self->main_context, g_main_context_unref); if (!self->name_is_static) g_clear_pointer (&self->name, g_free); g_mutex_clear (&self->mutex); G_OBJECT_CLASS (dex_async_result_parent_class)->finalize (object); } static void dex_async_result_class_init (DexAsyncResultClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = dex_async_result_finalize; } static void dex_async_result_init (DexAsyncResult *self) { g_mutex_init (&self->mutex); } const char * dex_async_result_get_name (DexAsyncResult *self) { g_return_val_if_fail (DEX_IS_ASYNC_RESULT (self), NULL); return self->name; } void dex_async_result_set_name (DexAsyncResult *async_result, const char *name) { g_return_if_fail (DEX_IS_ASYNC_RESULT (async_result)); g_mutex_lock (&async_result->mutex); if (async_result->name == NULL) async_result->name = g_strdup (name); g_mutex_unlock (&async_result->mutex); } void dex_async_result_set_static_name (DexAsyncResult *async_result, const char *name) { DexAsyncResult *self = DEX_ASYNC_RESULT (async_result); g_return_if_fail (DEX_IS_ASYNC_RESULT (self)); g_mutex_lock (&self->mutex); if (self->name == NULL) { self->name = (gpointer)name; self->name_is_static = TRUE; } g_mutex_unlock (&self->mutex); } /** * dex_async_result_dup_future: * @async_result: a #DexAsyncResult * * Gets the future for the #DexAsyncResult, or %NULL if a future * is not available. * * Returns: (transfer full) (nullable): a #DexFuture or %NULL */ DexFuture * dex_async_result_dup_future (DexAsyncResult *async_result) { DexFuture *future = NULL; g_return_val_if_fail (DEX_IS_ASYNC_RESULT (async_result), NULL); g_mutex_lock (&async_result->mutex); if (async_result->future != NULL) future = dex_ref (async_result->future); g_mutex_unlock (&async_result->mutex); return g_steal_pointer (&future); } static const GValue * dex_async_result_propagate (DexAsyncResult *async_result, GError **error) { const GValue *value = NULL; DexFuture *future; g_assert (DEX_IS_ASYNC_RESULT (async_result)); if ((future = dex_async_result_dup_future (async_result))) { value = dex_future_get_value (future, error); dex_unref (future); return value; } g_set_error (error, DEX_ERROR, DEX_ERROR_PENDING, "Future pending"); return NULL; } gpointer dex_async_result_propagate_pointer (DexAsyncResult *async_result, GError **error) { const GValue *value; g_return_val_if_fail (DEX_IS_ASYNC_RESULT (async_result), NULL); if ((value = dex_async_result_propagate (async_result, error))) { if (G_VALUE_HOLDS_OBJECT (value)) return g_value_dup_object (value); else if (G_VALUE_HOLDS_BOXED (value)) return g_value_dup_boxed (value); else if (G_VALUE_HOLDS_VARIANT (value)) return g_value_dup_variant (value); else if (G_VALUE_HOLDS_POINTER (value)) return g_value_get_pointer (value); else g_critical ("Cannot propagate pointer of type %s", G_VALUE_TYPE_NAME (value)); } return NULL; } gssize dex_async_result_propagate_int (DexAsyncResult *async_result, GError **error) { const GValue *value; g_return_val_if_fail (DEX_IS_ASYNC_RESULT (async_result), 0); if ((value = dex_async_result_propagate (async_result, error))) { if (G_VALUE_HOLDS_INT (value)) return g_value_get_int (value); else if (G_VALUE_HOLDS_UINT (value)) return g_value_get_uint (value); else if (G_VALUE_HOLDS_INT64 (value)) return g_value_get_int64 (value); else if (G_VALUE_HOLDS_UINT64 (value)) return g_value_get_uint64 (value); else if (G_VALUE_HOLDS_LONG (value)) return g_value_get_long (value); else if (G_VALUE_HOLDS_ULONG (value)) return g_value_get_ulong (value); else g_critical ("Cannot propagate int from type %s", G_VALUE_TYPE_NAME (value)); } return 0; } double dex_async_result_propagate_double (DexAsyncResult *async_result, GError **error) { const GValue *value; g_return_val_if_fail (DEX_IS_ASYNC_RESULT (async_result), 0); if ((value = dex_async_result_propagate (async_result, error))) { if (G_VALUE_HOLDS_DOUBLE (value)) return g_value_get_double (value); else if (G_VALUE_HOLDS_FLOAT (value)) return g_value_get_float (value); } return .0; } gboolean dex_async_result_propagate_boolean (DexAsyncResult *async_result, GError **error) { const GValue *value; g_return_val_if_fail (DEX_IS_ASYNC_RESULT (async_result), FALSE); if ((value = dex_async_result_propagate (async_result, error))) { if (G_VALUE_HOLDS_BOOLEAN (value)) return g_value_get_boolean (value); g_critical ("%s() got future of type %s, expected gboolean", G_STRFUNC, G_VALUE_TYPE_NAME (value)); return FALSE; } return FALSE; } static gboolean dex_async_result_complete_in_idle_cb (gpointer user_data) { DexAsyncResult *async_result = user_data; g_assert (DEX_IS_ASYNC_RESULT (async_result)); async_result->callback (async_result->source_object, G_ASYNC_RESULT (async_result), g_steal_pointer (&async_result->user_data)); dex_clear (&async_result->future); return G_SOURCE_REMOVE; } static DexFuture * dex_async_result_await_cb (DexFuture *future, gpointer user_data) { DexAsyncResult *async_result = user_data; g_assert (DEX_IS_FUTURE (future)); g_assert (DEX_IS_ASYNC_RESULT (async_result)); g_assert (async_result->await_once); g_assert (async_result->main_context != NULL); g_mutex_lock (&async_result->mutex); g_assert (!async_result->await_returned); if (async_result->callback != NULL) { GSource *idle_source = g_idle_source_new (); g_source_set_priority (idle_source, async_result->priority); g_source_set_callback (idle_source, dex_async_result_complete_in_idle_cb, g_object_ref (async_result), g_object_unref); if (async_result->name_is_static) _g_source_set_static_name (idle_source, async_result->name); else g_source_set_name (idle_source, async_result->name); g_source_attach (idle_source, async_result->main_context); g_source_unref (idle_source); } async_result->await_returned = TRUE; g_mutex_unlock (&async_result->mutex); return NULL; } /** * dex_async_result_await: * @async_result: a #GAsyncResult * @future: (transfer full): a #DexFuture * * Tracks the result of @future and uses the value to complete @async_result, * eventually calling the registered #GAsyncReadyCallback. */ void dex_async_result_await (DexAsyncResult *async_result, DexFuture *future) { DexFuture *new_future = NULL; DexFuture *cancellable; g_return_if_fail (DEX_IS_ASYNC_RESULT (async_result)); g_return_if_fail (DEX_IS_FUTURE (future)); g_mutex_lock (&async_result->mutex); if G_UNLIKELY (async_result->await_once != FALSE) { g_mutex_unlock (&async_result->mutex); g_critical ("%s() called more than once on %s @ %p [%s]", G_STRFUNC, G_OBJECT_TYPE_NAME (async_result), async_result, async_result->name ? async_result->name : "unnamed task"); return; } async_result->await_once = TRUE; g_mutex_unlock (&async_result->mutex); if (async_result->cancellable == NULL) cancellable = NULL; else cancellable = dex_cancellable_new_from_cancellable (async_result->cancellable); /* We have to be careful about ownership here and ensure we * drop our references when the callback is executed. */ g_object_ref (async_result); new_future = dex_future_finally (dex_future_first (future, cancellable, NULL), dex_async_result_await_cb, g_object_ref (async_result), g_object_unref); g_mutex_lock (&async_result->mutex); g_assert (async_result->future == NULL); async_result->future = g_steal_pointer (&new_future); g_mutex_unlock (&async_result->mutex); g_object_unref (async_result); } void dex_async_result_set_priority (DexAsyncResult *async_result, int priority) { g_return_if_fail (DEX_IS_ASYNC_RESULT (async_result)); g_mutex_lock (&async_result->mutex); async_result->priority = priority; g_mutex_unlock (&async_result->mutex); } 07070100000029000081A40000000000000000000000016712D33C00000CBA000000000000000000000000000000000000002400000000libdex-0.8.1/src/dex-async-result.h/* dex-async-result.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gio/gio.h> #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_ASYNC_RESULT (dex_async_result_get_type()) DEX_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (DexAsyncResult, dex_async_result, DEX, ASYNC_RESULT, GObject) DEX_AVAILABLE_IN_ALL DexAsyncResult *dex_async_result_new (gpointer source_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL void dex_async_result_set_priority (DexAsyncResult *async_result, int priority); DEX_AVAILABLE_IN_ALL const char *dex_async_result_get_name (DexAsyncResult *async_result); DEX_AVAILABLE_IN_ALL void dex_async_result_set_name (DexAsyncResult *async_result, const char *name); DEX_AVAILABLE_IN_ALL void dex_async_result_set_static_name (DexAsyncResult *async_result, const char *name); DEX_AVAILABLE_IN_ALL void dex_async_result_await (DexAsyncResult *async_result, DexFuture *future); DEX_AVAILABLE_IN_ALL gpointer dex_async_result_propagate_pointer (DexAsyncResult *async_result, GError **error) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL gboolean dex_async_result_propagate_boolean (DexAsyncResult *async_result, GError **error); DEX_AVAILABLE_IN_ALL gssize dex_async_result_propagate_int (DexAsyncResult *async_result, GError **error); DEX_AVAILABLE_IN_ALL double dex_async_result_propagate_double (DexAsyncResult *async_result, GError **error); DEX_AVAILABLE_IN_ALL DexFuture *dex_async_result_dup_future (DexAsyncResult *async_result) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS 0707010000002A000081A40000000000000000000000016712D33C000004E5000000000000000000000000000000000000002500000000libdex-0.8.1/src/dex-block-private.h/* * dex-block-private.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future-private.h" #include "dex-block.h" G_BEGIN_DECLS DexFuture *dex_block_new (DexFuture *future, DexScheduler *scheduler, DexBlockKind kind, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy); G_END_DECLS 0707010000002B000081A40000000000000000000000016712D33C000023C8000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-block.c/* * dex-block.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-block-private.h" #include "dex-thread-storage-private.h" /** * DexBlock: * * #DexBlock represents a callback closure that can be scheduled to run * within a specific #GMainContext. * * You create these by chaining futures together using dex_future_then(), * dex_future_catch(), dex_future_finally() and similar. */ typedef struct _DexBlock { DexFuture parent_instance; DexScheduler *scheduler; DexFuture *awaiting; DexFutureCallback callback; gpointer callback_data; GDestroyNotify callback_data_destroy; DexBlockKind kind : 3; guint handled : 1; } DexBlock; typedef struct _DexBlockClass { DexFutureClass parent_class; } DexBlockClass; DEX_DEFINE_FINAL_TYPE (DexBlock, dex_block, DEX_TYPE_FUTURE) G_DEFINE_ENUM_TYPE (DexBlockKind, dex_block_kind, G_DEFINE_ENUM_VALUE (DEX_BLOCK_KIND_THEN, "then"), G_DEFINE_ENUM_VALUE (DEX_BLOCK_KIND_CATCH, "catch"), G_DEFINE_ENUM_VALUE (DEX_BLOCK_KIND_FINALLY, "finally")) #undef DEX_TYPE_BLOCK #define DEX_TYPE_BLOCK dex_block_type static gboolean dex_block_handles (DexBlock *block, DexFuture *future) { g_assert (DEX_IS_BLOCK (block)); g_assert (DEX_IS_FUTURE (future)); switch (dex_future_get_status (future)) { case DEX_FUTURE_STATUS_RESOLVED: return (block->kind & DEX_BLOCK_KIND_THEN) != 0; case DEX_FUTURE_STATUS_REJECTED: return (block->kind & DEX_BLOCK_KIND_CATCH) != 0; case DEX_FUTURE_STATUS_PENDING: default: return FALSE; } } typedef struct _PropagateState { DexBlock *block; DexFuture *completed; } PropagateState; static gboolean dex_block_propagate_within_scheduler_internal (PropagateState *state) { DexFuture *delayed = state->block->callback (state->completed, state->block->callback_data); /* If we got a future then we need to chain to it so that we get * a second propagation callback with the resolved or rejected * value for resolution. */ if (delayed != NULL) { dex_object_lock (state->block); state->block->awaiting = dex_ref (delayed); dex_object_unlock (state->block); dex_future_chain (delayed, DEX_FUTURE (state->block)); dex_unref (delayed); return TRUE; } else { GDestroyNotify notify = NULL; gpointer notify_data = NULL; /* We can't asynchronously wait for more futures now so we should * aggressively release the callback data so that any reference cycles * are broken immediately. */ dex_object_lock (state->block); notify = g_steal_pointer (&state->block->callback_data_destroy); notify_data = g_steal_pointer (&state->block->callback_data); state->block->callback = NULL; dex_object_unlock (state->block); if (notify != NULL) notify (notify_data); return FALSE; } } static void dex_block_propagate_within_scheduler (gpointer data) { PropagateState *state = data; g_assert (state != NULL); g_assert (DEX_IS_BLOCK (state->block)); g_assert (DEX_IS_FUTURE (state->completed)); if (!dex_block_propagate_within_scheduler_internal (state)) dex_future_complete (DEX_FUTURE (state->block), state->completed->rejected ? NULL : &state->completed->resolved, state->completed->rejected ? g_error_copy (state->completed->rejected) : NULL); dex_clear (&state->block); dex_clear (&state->completed); g_free (state); } static gboolean dex_block_propagate (DexFuture *future, DexFuture *completed) { DexBlock *block = DEX_BLOCK (future); DexFuture *awaiting; gboolean do_callback = FALSE; g_assert (DEX_IS_BLOCK (block)); g_assert (DEX_IS_FUTURE (completed)); g_assert (dex_future_get_status (completed) != DEX_FUTURE_STATUS_PENDING); /* Mark result as handled as we don't want to execute the callback * again when a possible secondary DexFuture propagates completion * to us. That would only happen if @callback returns a DexFuture * which completes. */ dex_object_lock (future); if ((block->kind & DEX_BLOCK_KIND_LOOP) != 0) do_callback = TRUE; else if (!block->handled) do_callback = block->handled = TRUE; awaiting = g_steal_pointer (&block->awaiting); dex_object_unlock (future); /* Release the future we were waiting on */ dex_clear (&awaiting); /* Run the callback, possibly getting a future back to delay further * processing until it's completed. */ if (do_callback && dex_block_handles (block, completed)) { PropagateState state = {block, completed}; DexThreadStorage *storage = dex_thread_storage_get (); /* If we are on the same scheduler that created this block, then * we can execute it now. */ if (block->scheduler == dex_scheduler_get_thread_default () && storage->sync_dispatch_depth < DEX_DISPATCH_RECURSE_MAX) { gboolean ret; storage->sync_dispatch_depth++; ret = dex_block_propagate_within_scheduler_internal (&state); storage->sync_dispatch_depth--; return ret; } /* Otherwise we must defer it to the scheduler */ dex_ref (block); dex_ref (completed); dex_scheduler_push (block->scheduler, dex_block_propagate_within_scheduler, g_memdup2 (&state, sizeof state)); return TRUE; } return FALSE; } static void dex_block_finalize (DexObject *object) { DexBlock *block = DEX_BLOCK (object); if (block->callback_data_destroy) { block->callback_data_destroy (block->callback_data); block->callback_data_destroy = NULL; block->callback_data = NULL; block->callback = NULL; } dex_clear (&block->awaiting); dex_clear (&block->scheduler); DEX_OBJECT_CLASS (dex_block_parent_class)->finalize (object); } static void dex_block_class_init (DexBlockClass *block_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (block_class); DexFutureClass *future_class = DEX_FUTURE_CLASS (block_class); object_class->finalize = dex_block_finalize; future_class->propagate = dex_block_propagate; } static void dex_block_init (DexBlock *block) { } /** * dex_block_new: * @future: (transfer full): a #DexFuture to process * @scheduler: (nullable): a #DexScheduler or %NULL * @kind: the kind of block * @callback: (scope notified): the callback for the block * @callback_data: the data for the callback * @callback_data_destroy: closure destroy for @callback_data * * Creates a new block that will process the result of @future. * * The result of @callback will be assigned to the future returned * from this method. * * Returns: (transfer full): a #DexBlock */ DexFuture * dex_block_new (DexFuture *future, DexScheduler *scheduler, DexBlockKind kind, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) { DexBlock *block; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); block = (DexBlock *)dex_object_create_instance (dex_block_type); block->scheduler = scheduler ? dex_ref (scheduler) : dex_scheduler_ref_thread_default (); block->awaiting = future; block->kind = kind; block->callback = callback; block->callback_data = callback_data; block->callback_data_destroy = callback_data_destroy; dex_future_chain (future, DEX_FUTURE (block)); return DEX_FUTURE (block); } /** * dex_block_get_kind: * @block: a #DexBlock * * Gets the kind of block. * * The kind of block relates to what situations the block would be * executed such as for handling a future resolution, rejection, or * both. * * Returns: a #DexBlockKind */ DexBlockKind dex_block_get_kind (DexBlock *block) { g_return_val_if_fail (DEX_IS_BLOCK (block), 0); return block->kind; } /** * dex_block_get_scheduler: * @block: a #DexBlock * * Gets the scheduler to use when executing a block. * * Returns: (transfer none): a #DexScheduler */ DexScheduler * dex_block_get_scheduler (DexBlock *block) { g_return_val_if_fail (DEX_IS_BLOCK (block), NULL); return block->scheduler; } 0707010000002C000081A40000000000000000000000016712D33C0000073C000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-block.h/* * dex-block.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined (DEX_INSIDE) && !defined (DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif #include "dex-future.h" #include "dex-scheduler.h" G_BEGIN_DECLS #define DEX_TYPE_BLOCK (dex_block_get_type()) #define DEX_BLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_BLOCK, DexBlock)) #define DEX_IS_BLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_BLOCK)) typedef enum _DexBlockKind { DEX_BLOCK_KIND_THEN = 1 << 0, DEX_BLOCK_KIND_CATCH = 1 << 1, DEX_BLOCK_KIND_LOOP = 1 << 2, DEX_BLOCK_KIND_FINALLY = DEX_BLOCK_KIND_THEN | DEX_BLOCK_KIND_CATCH, } DexBlockKind; typedef struct _DexBlock DexBlock; DEX_AVAILABLE_IN_ALL GType dex_block_kind_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL GType dex_block_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexBlockKind dex_block_get_kind (DexBlock *block); DEX_AVAILABLE_IN_ALL DexScheduler *dex_block_get_scheduler (DexBlock *block); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexBlock, dex_unref) G_END_DECLS 0707010000002D000081A40000000000000000000000016712D33C00000F56000000000000000000000000000000000000002300000000libdex-0.8.1/src/dex-cancellable.c/* * dex-cancellable.c * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gio/gio.h> #include "dex-cancellable.h" #include "dex-future-private.h" /** * DexCancellable: * * `DexCancellable` is a simple cancellation primitive which allows * for you to create `DexFuture` that will reject upon cancellation. * * Use this combined with other futures using dex_future_all_race() * to create a future that resolves when all other futures complete * or `dex_cancellable_cancel()` is called to reject. */ typedef struct _DexCancellable { DexFuture parent_instance; GCancellable *cancellable; gulong handler; } DexCancellable; typedef struct _DexCancellableClass { DexFutureClass parent_class; } DexCancellableClass; DEX_DEFINE_FINAL_TYPE (DexCancellable, dex_cancellable, DEX_TYPE_FUTURE) static void dex_cancellable_finalize (DexObject *object) { DexCancellable *self = (DexCancellable *)object; if (self->handler) g_cancellable_disconnect (self->cancellable, self->handler); g_clear_object (&self->cancellable); DEX_OBJECT_CLASS (dex_cancellable_parent_class)->finalize (object); } static void dex_cancellable_class_init (DexCancellableClass *cancellable_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (cancellable_class); object_class->finalize = dex_cancellable_finalize; } static void dex_cancellable_init (DexCancellable *cancellable) { } DexCancellable * dex_cancellable_new (void) { return (DexCancellable *)dex_object_create_instance (DEX_TYPE_CANCELLABLE); } static void dex_cancellable_cancelled_cb (GCancellable *cancellable, DexWeakRef *wr) { g_autoptr(DexCancellable) self = NULL; g_assert (G_IS_CANCELLABLE (cancellable)); g_assert (wr != NULL); if ((self = dex_weak_ref_get (wr))) { self->handler = 0; dex_future_complete (DEX_FUTURE (self), NULL, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Operation cancelled")); } } static void weak_ref_free (gpointer data) { dex_weak_ref_clear (data); g_free (data); } DexFuture * dex_cancellable_new_from_cancellable (GCancellable *cancellable) { DexWeakRef *wr; DexCancellable *ret; g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), NULL); ret = dex_cancellable_new (); wr = g_new0 (DexWeakRef, 1); dex_weak_ref_init (wr, ret); ret->cancellable = g_object_ref (cancellable); ret->handler = g_cancellable_connect (cancellable, G_CALLBACK (dex_cancellable_cancelled_cb), wr, weak_ref_free); return DEX_FUTURE (ret); } void dex_cancellable_cancel (DexCancellable *cancellable) { g_return_if_fail (DEX_IS_CANCELLABLE (cancellable)); dex_future_complete (DEX_FUTURE (cancellable), NULL, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Operation cancelled")); } 0707010000002E000081A40000000000000000000000016712D33C00000702000000000000000000000000000000000000002300000000libdex-0.8.1/src/dex-cancellable.h/* * dex-cancellable.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined (DEX_INSIDE) && !defined (DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_CANCELLABLE (dex_cancellable_get_type()) #define DEX_CANCELLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_CANCELLABLE, DexCancellable)) #define DEX_IS_CANCELLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_CANCELLABLE)) typedef struct _DexCancellable DexCancellable; DEX_AVAILABLE_IN_ALL GType dex_cancellable_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexCancellable *dex_cancellable_new (void) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_cancellable_new_from_cancellable (GCancellable *cancellable) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL void dex_cancellable_cancel (DexCancellable *cancellable); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexCancellable, dex_unref) G_END_DECLS 0707010000002F000081A40000000000000000000000016712D33C00003E07000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-channel.c/* * dex-channel.c * * Copyright 2022-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gio/gio.h> #include "dex-error.h" #include "dex-channel.h" #include "dex-future-private.h" #include "dex-object-private.h" #include "dex-promise.h" static GError channel_closed_error; static GValue success_value; typedef enum _DexChannelStateFlags { DEX_CHANNEL_STATE_CAN_SEND = 1 << 0, DEX_CHANNEL_STATE_CAN_RECEIVE = 1 << 1, } DexChannelStateFlags; typedef struct _DexChannelReceiver { DexFuture parent_instance; GList link; } DexChannelReceiver; typedef struct _DexChannelReceiverClass { DexFutureClass parent_class; } DexChannelReceiverClass; #define DEX_IS_CHANNEL_RECEIVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, dex_channel_receiver_type)) GType dex_channel_receiver_get_type (void) G_GNUC_CONST; DEX_DEFINE_FINAL_TYPE (DexChannelReceiver, dex_channel_receiver, DEX_TYPE_FUTURE) static inline guint steal_uint (guint *value) { guint ret = *value; *value = 0; return ret; } static inline GQueue steal_queue (GQueue *queue) { return (GQueue) { .head = g_steal_pointer (&queue->head), .tail = g_steal_pointer (&queue->tail), .length = steal_uint (&queue->length), }; } static void dex_channel_receiver_class_init (DexChannelReceiverClass *channel_receiver_class) { success_value = (GValue) {G_TYPE_BOOLEAN, {{.v_int = TRUE}}}; } static void dex_channel_receiver_init (DexChannelReceiver *channel_receiver) { channel_receiver->link.data = channel_receiver; } static inline void dex_channel_receiver_complete (DexChannelReceiver *channel_receiver, gboolean success) { dex_future_complete ((DexFuture *)channel_receiver, success ? &success_value : NULL, success ? NULL : g_error_copy (&channel_closed_error)); } static inline DexChannelReceiver * dex_channel_receiver_new (void) { return (DexChannelReceiver *)dex_object_create_instance (dex_channel_receiver_type); } struct _DexChannel { DexObject parent_instance; /* Queue of senders waiting to insert an item in queue once the * capacity threshold has reduced. */ GQueue sendq; /* Queue of receivers waiting for an item to be inserted into the * queue so they can have it. */ GQueue recvq; /* The actual queue of items in the channel that have been sent and * not yet picked up by a receiver. */ GQueue queue; /* The max capacity of @queue. Both the @sendq and @recvq can be * greater than this as those represent callers and their futures. */ guint capacity; /* Flags indicating what sides of the channel are open/closed */ DexChannelStateFlags flags : 2; }; typedef struct _DexChannelClass { DexObjectClass parent_class; } DexChannelClass; typedef struct _DexChannelItem { /* Used to insert the item into queues */ GList link; /* The future which is provided to the caller of dex_channel_send(). */ DexPromise *send; /* The future which was sent with dex_channel_send(). */ DexFuture *future; } DexChannelItem; DEX_DEFINE_FINAL_TYPE (DexChannel, dex_channel, DEX_TYPE_OBJECT) #undef DEX_TYPE_CHANNEL #define DEX_TYPE_CHANNEL dex_channel_type static void dex_channel_unset_state_flags (DexChannel *channel, DexChannelStateFlags flags); static DexChannelItem * dex_channel_item_new (DexFuture *future) { DexChannelItem *item; g_assert (DEX_IS_FUTURE (future)); item = g_new0 (DexChannelItem, 1); item->link.data = item; item->future = future; item->send = dex_promise_new (); return item; } static void dex_channel_item_free (DexChannelItem *item) { g_assert (item != NULL); g_assert (!item->future || DEX_IS_FUTURE (item->future)); g_assert (!item->send || DEX_IS_PROMISE (item->send)); g_assert (item->link.data == item); g_assert (item->link.prev == NULL); g_assert (item->link.next == NULL); dex_clear (&item->future); dex_clear (&item->send); g_free (item); } static void dex_channel_finalize (DexObject *object) { DexChannel *channel = DEX_CHANNEL (object); dex_channel_unset_state_flags (channel, (DEX_CHANNEL_STATE_CAN_SEND | DEX_CHANNEL_STATE_CAN_RECEIVE)); g_assert (channel->queue.length == 0); g_assert (channel->sendq.length == 0); g_assert (channel->recvq.length == 0); g_assert (channel->flags == 0); DEX_OBJECT_CLASS (dex_channel_parent_class)->finalize (object); } static void dex_channel_class_init (DexChannelClass *channel_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (channel_class); object_class->finalize = dex_channel_finalize; g_type_ensure (dex_channel_receiver_get_type ()); channel_closed_error = (GError) { .domain = DEX_ERROR, .code = DEX_ERROR_CHANNEL_CLOSED, .message = (gpointer)"Channel closed", }; } static void dex_channel_init (DexChannel *channel) { channel->capacity = G_MAXUINT; } /** * dex_channel_new: * @capacity: the channel queue depth or 0 for unlimited * * Creates a new #DexChannel. * * If capacity is non-zero, it can be used to limit the size of the channel * so that functions can asynchronously stall until items have been removed * from the channel. This is useful in buffering situations so that the * producer does not outpace the consumer. * * Returns: a new #DexChannel */ DexChannel * dex_channel_new (guint capacity) { DexChannel *channel; if (capacity == 0) capacity = G_MAXUINT; channel = (DexChannel *)dex_object_create_instance (DEX_TYPE_CHANNEL); channel->capacity = capacity; channel->flags = DEX_CHANNEL_STATE_CAN_SEND | DEX_CHANNEL_STATE_CAN_RECEIVE; return channel; } static inline gboolean has_capacity_locked (DexChannel *channel) { return channel->sendq.length == 0 && channel->queue.length < channel->capacity; } static void dex_channel_one_receive_and_unlock (DexChannel *channel) { DexChannelItem *item = NULL; DexChannelReceiver *recv = NULL; DexPromise *to_resolve = NULL; guint qlen = 0; g_assert (DEX_IS_CHANNEL (channel)); /* This function removes a single item from the head of the queue and * pairs it with a future that was delivered to a caller of * dex_channel_receive(). The #DexPromise they were returned is * completed using the future that was provided to dex_channel_send() * (which itself still may not be ready, but we must preserve ordering). */ if (channel->queue.length > 0 && channel->recvq.length > 0) { recv = g_queue_pop_head_link (&channel->recvq)->data; item = g_queue_pop_head_link (&channel->queue)->data; g_assert (DEX_IS_CHANNEL_RECEIVER (recv)); g_assert (item != NULL); /* Try to advance a @sendq item into @queue */ if (channel->sendq.length > 0 && channel->queue.length < channel->capacity) { DexChannelItem *sendq_item = g_queue_peek_head (&channel->sendq); g_queue_unlink (&channel->sendq, &sendq_item->link); g_queue_push_tail_link (&channel->queue, &sendq_item->link); qlen = channel->queue.length; to_resolve = dex_ref (sendq_item->send); } } dex_object_unlock (channel); g_assert (item == NULL || recv != NULL); if (item != NULL) { dex_future_chain (item->future, DEX_FUTURE (recv)); dex_channel_item_free (item); dex_unref (recv); } if (to_resolve != NULL) { dex_promise_resolve_uint (to_resolve, qlen); dex_unref (to_resolve); } } /** * dex_channel_send: * @channel: a #DexChannel * @future: (transfer full): a #DexFuture * * Queues @future into the channel. * * The other end of the channel can receive the future (or a future that will * eventually resolve to @future) using dex_channel_receive(). * * This function returns a #DexFuture that will resolve when the channels * capacity is low enough to queue more items. * * If the send side of the channel is closed, the returned #DexFuture will be * rejected with %DEX_ERROR_CHANNEL_CLOSED. * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_channel_send (DexChannel *channel, DexFuture *future) { const DexChannelStateFlags required = DEX_CHANNEL_STATE_CAN_SEND|DEX_CHANNEL_STATE_CAN_RECEIVE; DexChannelItem *item; DexFuture *ret; g_return_val_if_fail (DEX_IS_CHANNEL (channel), NULL); g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); item = dex_channel_item_new (g_steal_pointer (&future)); dex_object_lock (channel); if ((channel->flags & required) != required) { dex_object_unlock (channel); dex_channel_item_free (item); return DEX_FUTURE (dex_future_new_reject (DEX_ERROR, DEX_ERROR_CHANNEL_CLOSED, "Channel is closed")); } ret = dex_ref (item->send); /* Try to place future directly into @queue. Otherwise, place it in @sendq * and we'll process it when more items have been received. */ if (!has_capacity_locked (channel)) { g_queue_push_tail_link (&channel->sendq, &item->link); dex_object_unlock (channel); } else { g_queue_push_tail_link (&channel->queue, &item->link); dex_promise_resolve_uint (item->send, channel->queue.length); dex_channel_one_receive_and_unlock (channel); } return ret; } /** * dex_channel_receive: * @channel: a #DexChannel * * Receives the next item from the channel. * * The resulting future will resolve or reject when an item is available * to the channel or when send side has closed (in that order). * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_channel_receive (DexChannel *channel) { DexChannelReceiver *recv; g_return_val_if_fail (DEX_IS_CHANNEL (channel), NULL); recv = dex_channel_receiver_new (); dex_object_lock (channel); if ((channel->flags & DEX_CHANNEL_STATE_CAN_RECEIVE) == 0) goto reject_receive; /* If no more items can be sent, and there are no items immediately * to fulfill this request, then we have to reject as it can never * be fulfilled. */ if ((channel->flags & DEX_CHANNEL_STATE_CAN_SEND) == 0) { if (channel->queue.length + channel->sendq.length <= channel->recvq.length) goto reject_receive; } /* Enqueue this receiver and then flush a queued operation if possible */ dex_ref (recv); g_queue_push_tail_link (&channel->recvq, &recv->link); dex_channel_one_receive_and_unlock (channel); return DEX_FUTURE (recv); reject_receive: dex_object_unlock (channel); dex_channel_receiver_complete (recv, FALSE); return DEX_FUTURE (recv); } /** * dex_channel_receive_all: * @channel: a #DexChannel * * Will attempt to receive all items in the channel as a #DexResultSet. * * If the receive side of the channel is closed, then the future will * reject with an error. * * If there are items in the queue, then they will be returned as part * of a #DexResultSet containing each of the futures. * * Otherwise, a #DexFutureSet will be returned which will resolve or * reject when the next item is available in the channel (or the send * or receive sides are closed). * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_channel_receive_all (DexChannel *channel) { g_autoptr(GPtrArray) ret = NULL; GQueue stolen = G_QUEUE_INIT; g_return_val_if_fail (DEX_IS_CHANNEL (channel), NULL); ret = g_ptr_array_new_with_free_func (dex_unref); dex_object_lock (channel); if ((channel->flags & DEX_CHANNEL_STATE_CAN_RECEIVE) == 0) goto reject_receive; if(channel->queue.length == 0) goto wait_for_result; stolen = steal_queue (&channel->queue); for (const GList *iter = stolen.head; iter; iter = iter->next) { DexChannelItem *item = iter->data; g_ptr_array_add (ret, g_steal_pointer (&item->future)); } dex_object_unlock (channel); while (stolen.length > 0) dex_channel_item_free (g_queue_pop_head_link (&stolen)->data); return dex_future_allv ((DexFuture **)ret->pdata, ret->len); reject_receive: dex_object_unlock (channel); return dex_future_new_for_error (g_error_copy (&channel_closed_error)); wait_for_result: dex_object_unlock (channel); return dex_future_all (dex_channel_receive (channel), NULL); } static void dex_channel_unset_state_flags (DexChannel *channel, DexChannelStateFlags flags) { GQueue queue = G_QUEUE_INIT; GQueue sendq = G_QUEUE_INIT; GQueue recvq = G_QUEUE_INIT; GQueue trunc = G_QUEUE_INIT; g_assert (DEX_IS_CHANNEL (channel)); dex_object_lock (channel); /* If we need to close the send-side, do so now */ if (flags & DEX_CHANNEL_STATE_CAN_SEND) { guint pending = channel->sendq.length + channel->queue.length; channel->flags &= ~DEX_CHANNEL_STATE_CAN_SEND; while (channel->recvq.length > pending) g_queue_push_head_link (&trunc, g_queue_pop_tail_link (&channel->recvq)); } /* If we need to close the receive-side, do so now and steal * all of the items and promises that need to be rejected. */ if (flags & DEX_CHANNEL_STATE_CAN_RECEIVE) { channel->flags &= ~DEX_CHANNEL_STATE_CAN_RECEIVE; queue = steal_queue (&channel->queue); sendq = steal_queue (&channel->sendq); recvq = steal_queue (&channel->recvq); } dex_object_unlock (channel); while (recvq.length > 0) { DexChannelReceiver *recv = g_queue_pop_head_link (&recvq)->data; dex_channel_receiver_complete (recv, FALSE); dex_unref (recv); } while (trunc.length > 0) { DexChannelReceiver *recv = g_queue_pop_head_link (&trunc)->data; dex_channel_receiver_complete (recv, FALSE); dex_unref (recv); } while (queue.length > 0) { DexChannelItem *item = g_queue_pop_head_link (&queue)->data; dex_channel_item_free (item); } while (sendq.length > 0) { DexChannelItem *item = g_queue_pop_head_link (&sendq)->data; dex_promise_reject (item->send, g_error_copy (&channel_closed_error)); dex_channel_item_free (item); } } void dex_channel_close_send (DexChannel *channel) { g_return_if_fail (DEX_IS_CHANNEL (channel)); dex_channel_unset_state_flags (channel, DEX_CHANNEL_STATE_CAN_SEND); } void dex_channel_close_receive (DexChannel *channel) { g_return_if_fail (DEX_IS_CHANNEL (channel)); dex_channel_unset_state_flags (channel, DEX_CHANNEL_STATE_CAN_RECEIVE); } gboolean dex_channel_can_send (DexChannel *channel) { gboolean ret; g_return_val_if_fail (DEX_IS_CHANNEL (channel), FALSE); dex_object_lock (channel); ret = (channel->flags & DEX_CHANNEL_STATE_CAN_SEND) != 0; dex_object_unlock (channel); return ret; } gboolean dex_channel_can_receive (DexChannel *channel) { gboolean ret; g_return_val_if_fail (DEX_IS_CHANNEL (channel), FALSE); dex_object_lock (channel); ret = (channel->flags & DEX_CHANNEL_STATE_CAN_RECEIVE) != 0; dex_object_unlock (channel); return ret; } 07070100000030000081A40000000000000000000000016712D33C0000085E000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-channel.h/* * dex-channel.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_CHANNEL (dex_channel_get_type()) #define DEX_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_CHANNEL, DexChannel)) #define DEX_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_CHANNEL)) typedef struct _DexChannel DexChannel; DEX_AVAILABLE_IN_ALL GType dex_channel_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexChannel *dex_channel_new (guint capacity) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_channel_send (DexChannel *channel, DexFuture *future) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_channel_receive (DexChannel *channel) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_channel_receive_all (DexChannel *channel) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL void dex_channel_close_send (DexChannel *channel); DEX_AVAILABLE_IN_ALL void dex_channel_close_receive (DexChannel *channel); DEX_AVAILABLE_IN_ALL gboolean dex_channel_can_send (DexChannel *channel); DEX_AVAILABLE_IN_ALL gboolean dex_channel_can_receive (DexChannel *channel); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexChannel, dex_unref) G_END_DECLS 07070100000031000081A40000000000000000000000016712D33C0000108E000000000000000000000000000000000000002600000000libdex-0.8.1/src/dex-compat-private.h/* dex-compat-private.h * * Copyright 2022 Christian Hergert <christian@hergert.me> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <errno.h> #include <glib-object.h> #if !GLIB_CHECK_VERSION(2, 72, 0) # ifndef _ISOC11_SOURCE # define _ISOC11_SOURCE # endif # include <stdlib.h> # include <string.h> #endif #ifdef G_OS_WIN32 # include <windows.h> # include <io.h> #endif G_BEGIN_DECLS #if !GLIB_CHECK_VERSION(2, 68, 0) static inline gpointer g_memdup2 (gconstpointer mem, gsize byte_size) { g_assert (byte_size <= G_MAXUINT); return g_memdup (mem, byte_size); } #endif static inline void _g_source_set_static_name (GSource *source, const char *name) { #if GLIB_CHECK_VERSION(2, 70, 0) g_source_set_static_name (source, name); #else g_source_set_name (source, name); #endif } #if !GLIB_CHECK_VERSION(2, 70, 0) # define G_DEFINE_FINAL_TYPE_WITH_CODE(TN, t_n, T_P, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, 0) {_C_;} _G_DEFINE_TYPE_EXTENDED_END() # define G_TYPE_FLAG_FINAL 0 # define G_TYPE_IS_FINAL(type) 1 #endif #if !GLIB_CHECK_VERSION(2, 73, 0) # define G_DEFINE_ENUM_VALUE(EnumValue, EnumNick) \ { EnumValue, #EnumValue, EnumNick } # define G_DEFINE_ENUM_TYPE(TypeName, type_name, ...) \ GType \ G_PASTE(type_name, _get_type) (void) \ { \ static gsize g_define_type__static = 0; \ if (g_once_init_enter (&g_define_type__static)) { \ static const GEnumValue enum_values[] = { \ __VA_ARGS__ , \ { 0, NULL, NULL }, \ }; \ GType g_define_type = g_enum_register_static (g_intern_static_string (G_STRINGIFY (TypeName)), enum_values); \ g_once_init_leave (&g_define_type__static, g_define_type); \ } \ return g_define_type__static; \ } #endif #if !GLIB_CHECK_VERSION(2, 72, 0) static inline gpointer g_aligned_alloc (gsize n_blocks, gsize n_block_bytes, gsize alignment) { gpointer mem = aligned_alloc (alignment, n_blocks * n_block_bytes); if (mem == NULL) g_error ("Failed to allocate %"G_GSIZE_FORMAT" bytes", n_blocks * n_block_bytes); return mem; } static inline gpointer g_aligned_alloc0 (gsize n_blocks, gsize n_block_bytes, gsize alignment) { gpointer mem = g_aligned_alloc (n_blocks, n_block_bytes, alignment); memset (mem, 0, n_blocks * n_block_bytes); return mem; } static inline void g_aligned_free (gpointer mem) { free (mem); } #endif #ifdef G_OS_WIN32 static inline gsize pread (int fd, gpointer buf, gsize count, goffset offset) { OVERLAPPED ov = {0}; DWORD n_read = 0; HANDLE hFile; hFile = (HANDLE)_get_osfhandle (fd); ov.OffsetHigh = (offset & 0xFFFFFFFF00000000LL) >> 32; ov.Offset = (offset & 0xFFFFFFFFLL); SetLastError (0); if (!ReadFile (hFile, buf, count, &n_read, &ov)) { int errsv = GetLastError (); if (errsv != ERROR_HANDLE_EOF) { errno = errsv; return -1; } } return n_read; } static inline gsize pwrite (int fd, gconstpointer buf, gsize count, goffset offset) { OVERLAPPED ov = {0}; DWORD n_written = 0; HANDLE hFile; hFile = (HANDLE)_get_osfhandle (fd); ov.OffsetHigh = (offset & 0xFFFFFFFF00000000LL) >> 32; ov.Offset = (offset & 0xFFFFFFFFLL); SetLastError (0); if (!WriteFile (hFile, buf, count, &n_written, &ov)) { errno = GetLastError (); return -1; } return n_written; } #endif G_END_DECLS 07070100000032000081A40000000000000000000000016712D33C000010A0000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-delayed.c/* * dex-delayed.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-delayed.h" #include "dex-future-private.h" /** * DexDelayed: * * #DexDelayed is a future which will resolve or reject the value of another * #DexFuture when dex_delayed_release() is called. * * This allows you to gate the resolution of a future which has already * resolved or rejected until a later moment. */ struct _DexDelayed { DexFuture parent_instance; DexFuture *future; guint corked : 1; }; typedef struct _DexDelayedClass { DexFutureClass parent_class; } DexDelayedClass; DEX_DEFINE_FINAL_TYPE (DexDelayed, dex_delayed, DEX_TYPE_FUTURE) static gboolean dex_delayed_propagate (DexFuture *future, DexFuture *completed) { DexDelayed *delayed = DEX_DELAYED (future); gboolean ret = FALSE; g_assert (DEX_IS_DELAYED (delayed)); g_assert (DEX_IS_FUTURE (completed)); dex_object_lock (delayed); if (delayed->corked) ret = TRUE; else dex_clear (&delayed->future); dex_object_unlock (delayed); return ret; } static void dex_delayed_discard (DexFuture *future) { DexDelayed *delayed = DEX_DELAYED (future); DexFuture *awaiting; g_assert (DEX_IS_DELAYED (delayed)); dex_object_lock (delayed); awaiting = g_steal_pointer (&delayed->future); dex_object_unlock (delayed); if (awaiting != NULL) { dex_future_discard (awaiting, future); dex_clear (&awaiting); } } static void dex_delayed_finalize (DexObject *object) { DexDelayed *delayed = DEX_DELAYED (object); dex_clear (&delayed->future); DEX_OBJECT_CLASS (dex_delayed_parent_class)->finalize (object); } static void dex_delayed_class_init (DexDelayedClass *delayed_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (delayed_class); DexFutureClass *future_class = DEX_FUTURE_CLASS (delayed_class); object_class->finalize = dex_delayed_finalize; future_class->propagate = dex_delayed_propagate; future_class->discard = dex_delayed_discard; } static void dex_delayed_init (DexDelayed *delayed) { } DexFuture * dex_delayed_new (DexFuture *future) { DexDelayed *delayed; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); delayed = (DexDelayed *)dex_object_create_instance (DEX_TYPE_DELAYED); delayed->corked = TRUE; delayed->future = dex_ref (future); dex_future_chain (DEX_FUTURE (delayed), future); return DEX_FUTURE (delayed); } void dex_delayed_release (DexDelayed *delayed) { DexFuture *complete = NULL; g_return_if_fail (DEX_IS_DELAYED (delayed)); dex_object_lock (delayed); if (delayed->corked) { delayed->corked = FALSE; complete = g_steal_pointer (&delayed->future); } dex_object_unlock (delayed); if (complete != NULL) { dex_future_complete_from (DEX_FUTURE (delayed), complete); dex_clear (&complete); } } /** * dex_delayed_dup_future: * @delayed: a #DexDelayed * * Retrieves the delayed future provided to dex_delayed_new(). * * This function can only return a #DexFuture before dex_delayed_release() * is called. After that, the delayed future is released and this function * will return %NULL. * * Returns: (transfer full) (nullable): a #DexFuture or %NULL */ DexFuture * dex_delayed_dup_future (DexDelayed *delayed) { DexFuture *ret; g_return_val_if_fail (DEX_IS_DELAYED (delayed), NULL); dex_object_lock (delayed); ret = delayed->future ? dex_ref (delayed->future) : NULL; dex_object_unlock (delayed); return ret; } 07070100000033000081A40000000000000000000000016712D33C0000061F000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-delayed.h/* * dex-delayed.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_DELAYED (dex_delayed_get_type()) #define DEX_DELAYED(object) (G_TYPE_CHECK_INSTANCE_CAST(object, DEX_TYPE_DELAYED, DexDelayed)) #define DEX_IS_DELAYED(object) (G_TYPE_CHECK_INSTANCE_TYPE(object, DEX_TYPE_DELAYED)) typedef struct _DexDelayed DexDelayed; DEX_AVAILABLE_IN_ALL GType dex_delayed_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexFuture *dex_delayed_new (DexFuture *future) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL void dex_delayed_release (DexDelayed *delayed); DEX_AVAILABLE_IN_ALL DexFuture *dex_delayed_dup_future (DexDelayed *delayed) G_GNUC_WARN_UNUSED_RESULT; G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexDelayed, dex_unref) G_END_DECLS 07070100000034000081A40000000000000000000000016712D33C0000049D000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-enums.c/* * dex-enums.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-compat-private.h" #include "dex-enums.h" G_DEFINE_ENUM_TYPE (DexFutureStatus, dex_future_status, G_DEFINE_ENUM_VALUE (DEX_FUTURE_STATUS_PENDING, "pending"), G_DEFINE_ENUM_VALUE (DEX_FUTURE_STATUS_RESOLVED, "resolved"), G_DEFINE_ENUM_VALUE (DEX_FUTURE_STATUS_REJECTED, "rejected")) 07070100000035000081A40000000000000000000000016712D33C00000514000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-enums.h/* * dex-enums.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined (DEX_INSIDE) && !defined (DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif #include <glib-object.h> #include "dex-version-macros.h" G_BEGIN_DECLS #define DEX_TYPE_FUTURE_STATUS (dex_future_status_get_type()) typedef enum _DexFutureStatus { DEX_FUTURE_STATUS_PENDING, DEX_FUTURE_STATUS_RESOLVED, DEX_FUTURE_STATUS_REJECTED, } DexFutureStatus; DEX_AVAILABLE_IN_ALL GType dex_future_status_get_type (void) G_GNUC_CONST; G_END_DECLS 07070100000036000081A40000000000000000000000016712D33C00000407000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-error.c/* * dex-error.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-error.h" GQuark dex_error_quark (void) { static GQuark error_quark; if G_UNLIKELY (error_quark == 0) error_quark = g_quark_from_static_string ("dex-error"); return error_quark; } 07070100000037000081A40000000000000000000000016712D33C0000051F000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-error.h/* * dex-error.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-version-macros.h" G_BEGIN_DECLS #define DEX_ERROR (dex_error_quark()) typedef enum _DexError { DEX_ERROR_UNKNOWN, DEX_ERROR_CHANNEL_CLOSED, DEX_ERROR_DEPENDENCY_FAILED, DEX_ERROR_FIBER_EXITED, DEX_ERROR_NO_FIBER, DEX_ERROR_PENDING, DEX_ERROR_SEMAPHORE_CLOSED, DEX_ERROR_TIMED_OUT, DEX_ERROR_TYPE_MISMATCH, DEX_ERROR_TYPE_NOT_SUPPORTED, DEX_ERROR_FIBER_CANCELLED, } DexError; DEX_AVAILABLE_IN_ALL GQuark dex_error_quark (void) G_GNUC_CONST; G_END_DECLS 07070100000038000081A40000000000000000000000016712D33C000016E4000000000000000000000000000000000000002D00000000libdex-0.8.1/src/dex-fiber-context-private.h/* dex-fiber-context-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <errno.h> #include <string.h> #include <glib.h> #include "dex-compat-private.h" #include "dex-platform.h" #include "dex-stack-private.h" #ifdef G_OS_UNIX # include "dex-ucontext-private.h" #endif #ifdef G_OS_WIN32 # include <windows.h> #endif G_BEGIN_DECLS typedef struct _DexFiberContextStart { GHookFunc func; gpointer data; } DexFiberContextStart; #ifdef G_OS_UNIX /* If the system we're on has an alignment requirement of > sizeof(void*) * then we will allocate aligned memory for the ucontext_t instead of * including it inline. Otherwise, g_type_create_instance() (which only will * guarantee, at least via public API descriptions) a structure that is * guaranteed to be aligned to sizeof(void*). * * This is an issue, at minimum, on FreeBSD where x86_64 has a 16-bytes * alignment requirement for ucontext_t. */ #if ALIGN_OF_UCONTEXT > GLIB_SIZEOF_VOID_P typedef ucontext_t *DexFiberContext; #else typedef ucontext_t DexFiberContext; #endif static inline void dex_fiber_context_switch (DexFiberContext *old_context, DexFiberContext *new_context); static inline void dex_fiber_context_start (guint start_lo, guint start_hi) { union { guintptr uval; DexFiberContextStart *ptr; } u; u.uval = 0; #if GLIB_SIZEOF_VOID_P == 8 u.uval |= start_hi; u.uval <<= 32; #endif u.uval |= start_lo; u.ptr->func (u.ptr->data); } static inline void _dex_fiber_context_makecontext (ucontext_t *ucontext, DexStack *stack, DexFiberContextStart *start) { guint start_lo; guint start_hi = 0; ucontext->uc_stack.ss_size = stack->size; ucontext->uc_stack.ss_sp = stack->ptr; ucontext->uc_link = 0; #if GLIB_SIZEOF_VOID_P == 8 start_lo = GPOINTER_TO_SIZE (start) & 0xFFFFFFFFF; start_hi = (GPOINTER_TO_SIZE (start) >> 32) & 0xFFFFFFFFF; #else start_lo = GPOINTER_TO_SIZE (start); start_hi = 0; #endif G_GNUC_BEGIN_IGNORE_DEPRECATIONS makecontext (ucontext, G_CALLBACK (dex_fiber_context_start), 2, start_lo, start_hi); G_GNUC_END_IGNORE_DEPRECATIONS } static inline void dex_fiber_context_init (DexFiberContext *context, DexStack *stack, DexFiberContextStart *start) { ucontext_t *ucontext; #if ALIGN_OF_UCONTEXT > GLIB_SIZEOF_VOID_P *context = g_aligned_alloc (1, sizeof (ucontext_t), ALIGN_OF_UCONTEXT); ucontext = *context; #else ucontext = context; #endif memset (ucontext, 0, sizeof *ucontext); G_GNUC_BEGIN_IGNORE_DEPRECATIONS getcontext (ucontext); G_GNUC_END_IGNORE_DEPRECATIONS /* If stack is NULL, then this is a context used to save state * such as from the original stack in the fiber scheduler. */ if (stack != NULL) _dex_fiber_context_makecontext (ucontext, stack, start); } static inline void dex_fiber_context_clear (DexFiberContext *context) { #if ALIGN_OF_UCONTEXT > GLIB_SIZEOF_VOID_P g_aligned_free (*context); #endif } static inline void dex_fiber_context_init_main (DexFiberContext *context) { dex_fiber_context_init (context, NULL, NULL); } static inline void dex_fiber_context_clear_main (DexFiberContext *context) { dex_fiber_context_clear (context); } static inline void dex_fiber_context_switch (DexFiberContext *old_context, DexFiberContext *new_context) { int r; G_GNUC_BEGIN_IGNORE_DEPRECATIONS #if ALIGN_OF_UCONTEXT > GLIB_SIZEOF_VOID_P r = swapcontext (*old_context, *new_context); #else r = swapcontext (old_context, new_context); #endif G_GNUC_END_IGNORE_DEPRECATIONS #ifndef G_DISABLE_ASSERT if G_UNLIKELY (r != 0) g_error ("swapcontext(): %s", g_strerror (errno)); #else (void)r; #endif } #endif #ifdef G_OS_WIN32 typedef LPVOID DexFiberContext; static inline void dex_fiber_context_init_main (DexFiberContext *context) { *context = ConvertThreadToFiber (0); } static inline void dex_fiber_context_clear_main (DexFiberContext *context) { *context = NULL; ConvertFiberToThread (); } static inline void dex_fiber_context_init (DexFiberContext *context, DexStack *stack, DexFiberContextStart *start) { *context = CreateFiberEx (dex_get_min_stack_size (), stack->size, FIBER_FLAG_FLOAT_SWITCH, (LPFIBER_START_ROUTINE)start->func, start->data); if (*context == NULL) g_printerr ("Failed to create fiber (stack %u) (start_func %p): %s", (guint)stack->size, start->func, g_strerror (GetLastError ())); } static inline void dex_fiber_context_clear (DexFiberContext *context) { DeleteFiber (*context); } static inline void dex_fiber_context_switch (DexFiberContext *old_context, DexFiberContext *new_context) { SwitchToFiber (*new_context); } #endif G_END_DECLS 07070100000039000081A40000000000000000000000016712D33C00000D3D000000000000000000000000000000000000002500000000libdex-0.8.1/src/dex-fiber-private.h/* dex-fiber-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #ifndef PACKAGE_VERSION # error "config.h must be included before dex-fiber-private.h" #endif #include <glib.h> #ifdef G_OS_UNIX # include "dex-ucontext-private.h" #endif #include "dex-fiber.h" #include "dex-fiber-context-private.h" #include "dex-future-private.h" #include "dex-scheduler.h" #include "dex-stack-private.h" G_BEGIN_DECLS typedef struct _DexFiberScheduler DexFiberScheduler; struct _DexFiber { DexFuture parent_instance; /* Augmented link placed in either runnable or waiting queue * of the DexFiberScheduler. */ GList link; /* Various flags for the fiber */ guint running : 1; guint runnable : 1; guint exited : 1; guint released : 1; guint cancelled : 1; /* The requested stack size */ gsize stack_size; /* The assigned stack */ DexStack *stack; /* The scheduler affinity */ DexFiberScheduler *fiber_scheduler; /* Origin function/data for the fiber */ DexFiberFunc func; gpointer func_data; GDestroyNotify func_data_destroy; /* Used to hook into the fiber during creation */ DexFiberContextStart hook; /* The saved context for switching. This is abstracted in * dex-fiber-context-private.h for the particular platform * and alignment constraints. */ DexFiberContext context; }; struct _DexFiberScheduler { GSource source; /* All fibers are in either runnable or blocked. A pointer to the * running fiber (which is also in runnable until it yields) is * provided to allow getting the current fiber from internal code. */ GMutex mutex; DexFiber *running; GQueue runnable; GQueue blocked; /* Pooling of unused thread stacks */ DexStackPool *stack_pool; /* The saved context for the thread, which we return to when a * fiber yields back to the scheduler. */ DexFiberContext context; /* We delay initialization until first iteration so that we don't * have to call ConvertThreadToFiber() from _start() (as that fails * on wine64). */ guint has_initialized : 1; }; DexFiberScheduler *dex_fiber_scheduler_new (void); DexFiber *dex_fiber_new (DexFiberFunc func, gpointer func_data, GDestroyNotify func_data_destroy, gsize stack_size); void dex_fiber_scheduler_register (DexFiberScheduler *fiber_scheduler, DexFiber *fiber); G_END_DECLS 0707010000003A000081A40000000000000000000000016712D33C00003915000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-fiber.c/* dex-fiber.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <glib.h> #include "dex-compat-private.h" #include "dex-error.h" #include "dex-fiber-private.h" #include "dex-object-private.h" #include "dex-platform.h" #include "dex-thread-storage-private.h" /** * DexFiber: * * #DexFiber is a fiber (or coroutine) which itself is a #DexFuture. * * When the fiber completes execution it will either resolve or reject the * with the result or error. * * You may treat a #DexFiber like any other #DexFuture which makes it simple * to integrate fibers into other processing chains. * * #DexFiber are provided their own stack seperate from a threads main stack, * They are automatically scheduled as necessary. * * Use dex_await() and similar functions to await the result of another future * within the fiber and the fiber will be suspended allowing another fiber to * run and/or the rest of the applications main loop. * * Once a fiber is created, it is pinned to that scheduler. Use * dex_scheduler_spawn() to create a fiber on a specific scheduler. */ typedef struct _DexFiberClass { DexFutureClass parent_class; } DexFiberClass; DEX_DEFINE_FINAL_TYPE (DexFiber, dex_fiber, DEX_TYPE_FUTURE) #undef DEX_TYPE_FIBER #define DEX_TYPE_FIBER dex_fiber_type static void dex_fiber_start (DexFiber *fiber); static DexFuture *cancelled_future; static void dex_fiber_discard (DexFuture *future) { DexFiber *fiber = DEX_FIBER (future); g_assert (DEX_IS_FIBER (fiber)); dex_object_lock (fiber); fiber->cancelled = TRUE; dex_object_unlock (fiber); } static gboolean dex_fiber_propagate (DexFuture *future, DexFuture *completed) { DexFiber *fiber = DEX_FIBER (future); GSource *source = NULL; g_assert (DEX_IS_FIBER (fiber)); g_assert (DEX_IS_FUTURE (completed)); dex_object_lock (fiber); g_mutex_lock (&fiber->fiber_scheduler->mutex); g_assert (!fiber->runnable); g_assert (!fiber->exited); fiber->runnable = TRUE; g_queue_unlink (&fiber->fiber_scheduler->blocked, &fiber->link); g_queue_push_tail_link (&fiber->fiber_scheduler->runnable, &fiber->link); if (dex_thread_storage_get ()->fiber_scheduler != fiber->fiber_scheduler) source = g_source_ref ((GSource *)fiber->fiber_scheduler); g_mutex_unlock (&fiber->fiber_scheduler->mutex); dex_object_unlock (fiber); if (source != NULL) { g_main_context_wakeup (g_source_get_context (source)); g_source_unref (source); } return TRUE; } static void dex_fiber_finalize (DexObject *object) { DexFiber *fiber = DEX_FIBER (object); g_assert (fiber->fiber_scheduler == NULL); g_assert (fiber->link.data == fiber); g_assert (fiber->link.prev == NULL); g_assert (fiber->link.next == NULL); g_assert (fiber->stack == NULL); dex_fiber_context_clear (&fiber->context); DEX_OBJECT_CLASS (dex_fiber_parent_class)->finalize (object); } static void dex_fiber_class_init (DexFiberClass *fiber_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (fiber_class); DexFutureClass *future_class = DEX_FUTURE_CLASS (fiber_class); object_class->finalize = dex_fiber_finalize; future_class->discard = dex_fiber_discard; future_class->propagate = dex_fiber_propagate; if (cancelled_future == NULL) cancelled_future = dex_future_new_reject (DEX_ERROR, DEX_ERROR_FIBER_CANCELLED, "The fiber was cancelled"); } static void dex_fiber_init (DexFiber *fiber) { fiber->link.data = fiber; fiber->hook.func = (GHookFunc)dex_fiber_start; fiber->hook.data = fiber; } static void dex_fiber_start (DexFiber *fiber) { DexFuture *future; future = fiber->func (fiber->func_data); g_assert (future != (DexFuture *)fiber); /* Await any future returned from the fiber */ if (future != NULL) { dex_await_borrowed (future, NULL); dex_future_complete_from (DEX_FUTURE (fiber), future); dex_unref (future); } else { dex_future_complete (DEX_FUTURE (fiber), NULL, g_error_new_literal (DEX_ERROR, DEX_ERROR_FIBER_EXITED, "The fiber exited without a result")); } /* Mark the fiber as exited */ fiber->exited = TRUE; /* Free func data if necessary * * TODO: We probably want to be able associate this data with a maincontext that * will free it up so things like GtkWidget are freed on main thread. */ if (fiber->func_data_destroy) { GDestroyNotify func_data_destroy = fiber->func_data_destroy; gpointer func_data = fiber->func_data; fiber->func = NULL; fiber->func_data = NULL; fiber->func_data_destroy = NULL; func_data_destroy (func_data); } /* Now suspend, resuming the scheduler */ dex_fiber_context_switch (&fiber->context, &fiber->fiber_scheduler->context); } DexFiber * dex_fiber_new (DexFiberFunc func, gpointer func_data, GDestroyNotify func_data_destroy, gsize stack_size) { DexFiber *fiber; g_return_val_if_fail (func != NULL, NULL); fiber = (DexFiber *)dex_object_create_instance (DEX_TYPE_FIBER); fiber->func = func; fiber->func_data = func_data; fiber->func_data_destroy = func_data_destroy; fiber->stack_size = stack_size; return fiber; } static void dex_fiber_ensure_stack (DexFiber *fiber, DexFiberScheduler *fiber_scheduler) { g_assert (DEX_IS_FIBER (fiber)); g_assert (fiber_scheduler != NULL); if (fiber->stack == NULL) { if (!fiber_scheduler->has_initialized) { fiber_scheduler->has_initialized = TRUE; dex_fiber_context_init_main (&fiber_scheduler->context); } if (fiber->stack_size == 0 || fiber->stack_size == fiber_scheduler->stack_pool->stack_size) fiber->stack = dex_stack_pool_acquire (fiber_scheduler->stack_pool); else fiber->stack = dex_stack_new (fiber->stack_size); dex_fiber_context_init (&fiber->context, fiber->stack, &fiber->hook); } } static gboolean dex_fiber_scheduler_check (GSource *source) { DexFiberScheduler *fiber_scheduler = (DexFiberScheduler *)source; gboolean ret; g_mutex_lock (&fiber_scheduler->mutex); ret = fiber_scheduler->runnable.length != 0; g_mutex_unlock (&fiber_scheduler->mutex); return ret; } static gboolean dex_fiber_scheduler_prepare (GSource *source, int *timeout) { *timeout = -1; return dex_fiber_scheduler_check (source); } static gboolean dex_fiber_scheduler_iteration (DexFiberScheduler *fiber_scheduler) { DexFiber *fiber; g_assert (fiber_scheduler != NULL); g_mutex_lock (&fiber_scheduler->mutex); if ((fiber = g_queue_peek_head (&fiber_scheduler->runnable))) { dex_ref (fiber); g_assert (fiber->fiber_scheduler == fiber_scheduler); fiber->running = TRUE; fiber_scheduler->running = fiber; dex_fiber_ensure_stack (fiber, fiber_scheduler); } g_mutex_unlock (&fiber_scheduler->mutex); if (fiber == NULL) return FALSE; dex_fiber_context_switch (&fiber_scheduler->context, &fiber->context); g_mutex_lock (&fiber_scheduler->mutex); fiber->running = FALSE; fiber_scheduler->running = NULL; if (!fiber->released && fiber->exited) { g_queue_unlink (&fiber_scheduler->runnable, &fiber->link); if (fiber->stack->size == fiber_scheduler->stack_pool->stack_size) dex_stack_pool_release (fiber_scheduler->stack_pool, g_steal_pointer (&fiber->stack)); else g_clear_pointer (&fiber->stack, dex_stack_free); fiber->fiber_scheduler = NULL; fiber->released = TRUE; dex_unref (fiber); } g_mutex_unlock (&fiber_scheduler->mutex); dex_unref (fiber); return TRUE; } static gboolean dex_fiber_scheduler_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { DexFiberScheduler *fiber_scheduler = (DexFiberScheduler *)source; guint max_iterations; g_assert (fiber_scheduler != NULL); /* Only process up to as many fibers that are currently in * the queue so that we don't exhaust the main loop endlessly * processing completed fibers w/o yielding to other GSource. */ max_iterations = MAX (1, fiber_scheduler->runnable.length); dex_thread_storage_get ()->fiber_scheduler = fiber_scheduler; while (max_iterations && dex_fiber_scheduler_iteration (fiber_scheduler)) max_iterations--; dex_thread_storage_get ()->fiber_scheduler = NULL; return G_SOURCE_CONTINUE; } static void dex_fiber_scheduler_finalize (GSource *source) { DexFiberScheduler *fiber_scheduler = (DexFiberScheduler *)source; g_clear_pointer (&fiber_scheduler->stack_pool, dex_stack_pool_free); g_mutex_clear (&fiber_scheduler->mutex); if (fiber_scheduler->has_initialized) { fiber_scheduler->has_initialized = FALSE; dex_fiber_context_clear_main (&fiber_scheduler->context); } } /** * dex_fiber_scheduler_new: * * Creates a #DexFiberScheduler. * * #DexFiberScheduler is a sub-scheduler to a #DexScheduler that can swap * into and schedule runnable #DexFiber. * * A #DexScheduler should have one of these #GSource attached to its * #GMainContext so that fibers can be executed there. When a thread * exits, its fibers may need to be migrated. Currently that is not * implemented as we do not yet destroy #DexThreadPoolWorker. */ DexFiberScheduler * dex_fiber_scheduler_new (void) { DexFiberScheduler *fiber_scheduler; static GSourceFuncs funcs = { .check = dex_fiber_scheduler_check, .prepare = dex_fiber_scheduler_prepare, .dispatch = dex_fiber_scheduler_dispatch, .finalize = dex_fiber_scheduler_finalize, }; fiber_scheduler = (DexFiberScheduler *)g_source_new (&funcs, sizeof *fiber_scheduler); _g_source_set_static_name ((GSource *)fiber_scheduler, "[dex-fiber-scheduler]"); g_mutex_init (&fiber_scheduler->mutex); fiber_scheduler->stack_pool = dex_stack_pool_new (0, 0, 0); return fiber_scheduler; } void dex_fiber_scheduler_register (DexFiberScheduler *fiber_scheduler, DexFiber *fiber) { g_assert (fiber_scheduler != NULL); g_assert (DEX_IS_FIBER (fiber)); dex_ref (fiber); g_mutex_lock (&fiber_scheduler->mutex); g_assert (fiber->link.data == fiber); g_assert (fiber->fiber_scheduler == NULL); g_assert (fiber->exited == FALSE); g_assert (fiber->running == FALSE); g_assert (fiber->runnable == FALSE); g_assert (fiber->released == FALSE); fiber->fiber_scheduler = fiber_scheduler; fiber->runnable = TRUE; g_queue_push_tail_link (&fiber_scheduler->runnable, &fiber->link); g_mutex_unlock (&fiber_scheduler->mutex); if (dex_thread_storage_get ()->fiber_scheduler != fiber_scheduler) g_main_context_wakeup (g_source_get_context ((GSource *)fiber_scheduler)); } static DexFiber * dex_fiber_current (void) { DexFiberScheduler *fiber_scheduler = dex_thread_storage_get ()->fiber_scheduler; return fiber_scheduler ? fiber_scheduler->running : NULL; } static inline void dex_fiber_await (DexFiber *fiber, DexFuture *future) { DexFiberScheduler *fiber_scheduler = fiber->fiber_scheduler; gboolean cancelled; g_assert (DEX_IS_FIBER (fiber)); /* Move fiber from runnable to blocked queue */ g_mutex_lock (&fiber_scheduler->mutex); fiber->runnable = FALSE; cancelled = fiber->cancelled; g_queue_unlink (&fiber_scheduler->runnable, &fiber->link); g_queue_push_tail_link (&fiber_scheduler->blocked, &fiber->link); g_mutex_unlock (&fiber_scheduler->mutex); /* Now request the future notify us of completion */ if (cancelled) dex_future_chain (cancelled_future, DEX_FUTURE (fiber)); else dex_future_chain (future, DEX_FUTURE (fiber)); /* Swap to the scheduler to continue processing fibers */ dex_fiber_context_switch (&fiber->context, &fiber_scheduler->context); } const GValue * dex_await_borrowed (DexFuture *future, GError **error) { DexFiber *fiber; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); if (dex_future_get_status (future) != DEX_FUTURE_STATUS_PENDING) return dex_future_get_value (future, error); if G_UNLIKELY (!(fiber = dex_fiber_current ())) { g_set_error_literal (error, DEX_ERROR, DEX_ERROR_NO_FIBER, "Not running on a fiber, cannot await"); return NULL; } dex_fiber_await (fiber, future); return dex_future_get_value (future, error); } /** * dex_await: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError, or %NULL * * Suspends the current #DexFiber and resumes when @future has completed. * * If @future is completed when this function is called, the fiber will handle * the result immediately. * * This function may only be called within a #DexFiber. To do otherwise will * return %FALSE and @error set to %DEX_ERROR_NO_FIBER. * * It is an error to call this function in a way that would cause * intermediate code to become invalid when resuming the stack. For example, * if a foreach-style function taking a callback was to suspend from the * callback, undefined behavior may occur such as thread-local-storage * having changed. * * Returns: %TRUE if the future resolved, otherwise %FALSE * and @error is set. */ gboolean dex_await (DexFuture *future, GError **error) { const GValue *value = dex_await_borrowed (future, error); dex_unref (future); return value != NULL; } 0707010000003B000081A40000000000000000000000016712D33C000004CD000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-fiber.h/* dex-fiber.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_FIBER (dex_fiber_get_type()) #define DEX_FIBER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_FIBER, DexFiber)) #define DEX_IS_FIBER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_FIBER)) typedef struct _DexFiber DexFiber; DEX_AVAILABLE_IN_ALL GType dex_fiber_get_type (void) G_GNUC_CONST; G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexFiber, dex_unref) G_END_DECLS 0707010000003C000081A40000000000000000000000016712D33C00000903000000000000000000000000000000000000002600000000libdex-0.8.1/src/dex-future-private.h/* * dex-future-private.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #ifndef PACKAGE_VERSION # error "config.h must be included before dex-future-private.h" #endif #include "dex-future.h" #include "dex-object-private.h" G_BEGIN_DECLS #define DEX_FUTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, DEX_TYPE_FUTURE, DexFutureClass)) #define DEX_FUTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, DEX_TYPE_FUTURE, DexFutureClass)) typedef struct _DexFuture { DexObject parent_instance; GValue resolved; GError *rejected; GQueue chained; const char *name; DexFutureStatus status : 2; } DexFuture; typedef struct _DexFutureClass { DexObjectClass parent_class; gboolean (*propagate) (DexFuture *future, DexFuture *completed); void (*discard) (DexFuture *future); } DexFutureClass; void dex_future_chain (DexFuture *future, DexFuture *chained); void dex_future_complete (DexFuture *future, const GValue *value, GError *error); void dex_future_complete_from (DexFuture *future, DexFuture *completed); void dex_future_discard (DexFuture *future, DexFuture *chained); const GValue *dex_await_borrowed (DexFuture *future, GError **error); G_END_DECLS 0707010000003D000081A40000000000000000000000016712D33C000006C8000000000000000000000000000000000000002A00000000libdex-0.8.1/src/dex-future-set-private.h/* * dex-future-set-private.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future-set.h" #include "dex-future-private.h" G_BEGIN_DECLS typedef enum _DexFutureSetFlags { DEX_FUTURE_SET_FLAGS_NONE = 0, /* Propagate first resolve/reject (use extra flags to specify) */ DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST = 1 << 0, /* with PROPAGATE_FIRST, propagates on first resolve */ DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE = 1 << 1, /* with PROPAGATE_FIRST, propagates on first reject */ DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT = 1 << 2, } DexFutureSetFlags; DexFutureSet *dex_future_set_new_va (DexFuture *first_future, va_list *args, DexFutureSetFlags flags); DexFutureSet *dex_future_set_new (DexFuture * const *futures, guint n_futures, DexFutureSetFlags flags); G_END_DECLS 0707010000003E000081A40000000000000000000000016712D33C00002CB1000000000000000000000000000000000000002200000000libdex-0.8.1/src/dex-future-set.c/* * dex-future-set.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gio/gio.h> #include "dex-error.h" #include "dex-future-set-private.h" /** * DexFutureSet: * * #DexFutureSet represents a set of #DexFuture. * * You may retrieve each underlying #DexFuture using * dex_future_set_get_future_at(). * * The #DexFutureStatus of of the #DexFutureSet depends on how the set * was created using dex_future_all(), dex_future_any(), and similar mmethods. */ typedef struct _DexFutureSet { DexFuture parent_instance; DexFuture **futures; guint n_futures; guint n_resolved; guint n_rejected; DexFutureSetFlags flags : 4; guint padding : 28; /* If n_futures <= 2, we use this instead of mallocing. We could * potentially get a 3rd without breaking 2-cachelines (128 bytes) * if we used a bit above to manage a union of futures/embedded. */ DexFuture *embedded[2]; } DexFutureSet; typedef struct _DexFutureSetClass { DexFutureClass parent_class; } DexFutureSetClass; DEX_DEFINE_FINAL_TYPE (DexFutureSet, dex_future_set, DEX_TYPE_FUTURE) #undef DEX_TYPE_FUTURE_SET #define DEX_TYPE_FUTURE_SET dex_future_set_type static GValue success_value = G_VALUE_INIT; static gboolean dex_future_set_propagate (DexFuture *future, DexFuture *completed) { DexFutureSet *future_set = DEX_FUTURE_SET (future); const GValue *resolved = NULL; DexFutureStatus status; gboolean do_discard = FALSE; GError *rejected = NULL; guint n_active = 0; g_assert (DEX_IS_FUTURE_SET (future_set)); g_assert (DEX_IS_FUTURE (completed)); g_assert (future != completed); dex_object_lock (future_set); /* Short-circuit if we've already returned a value */ if (future->status != DEX_FUTURE_STATUS_PENDING) { dex_object_unlock (future_set); return TRUE; } switch ((int)(status = dex_future_get_status (completed))) { case DEX_FUTURE_STATUS_RESOLVED: future_set->n_resolved++; break; case DEX_FUTURE_STATUS_REJECTED: future_set->n_rejected++; break; default: g_assert_not_reached (); } n_active = future_set->n_futures - future_set->n_rejected - future_set->n_resolved; resolved = dex_future_get_value (completed, &rejected); g_assert (future_set->n_rejected <= future_set->n_futures); g_assert (future_set->n_resolved <= future_set->n_futures); g_assert (future_set->n_resolved + future_set->n_rejected <= future_set->n_futures); g_assert (rejected != NULL || resolved != NULL); dex_object_unlock (future_set); if (n_active == 0) { do_discard = TRUE; if (resolved != NULL) { if (future_set->flags & DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE) dex_future_complete (future, resolved, NULL); else dex_future_complete (future, &success_value, NULL); } else { if (future_set->flags & DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT) dex_future_complete (future, NULL, g_steal_pointer (&rejected)); else dex_future_complete (future, NULL, g_error_new_literal (DEX_ERROR, DEX_ERROR_DEPENDENCY_FAILED, "Too many futures failed")); } } else if (future_set->flags & DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST) { do_discard = TRUE; if (resolved && (future_set->flags & DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE) != 0) dex_future_complete (future, resolved, NULL); else if (rejected && (future_set->flags & DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT) != 0) dex_future_complete (future, NULL, g_steal_pointer (&rejected)); else do_discard = FALSE; } g_clear_error (&rejected); if (do_discard) { if (dex_future_get_status (future) != DEX_FUTURE_STATUS_PENDING) { for (guint i = 0; i < future_set->n_futures; i++) dex_future_discard (future_set->futures[i], future); } } return TRUE; } static void dex_future_set_finalize (DexObject *object) { DexFutureSet *future_set = DEX_FUTURE_SET (object); for (guint i = 0; i < future_set->n_futures; i++) { DexFuture *future = g_steal_pointer (&future_set->futures[i]); if (future != NULL) { dex_future_discard (future, DEX_FUTURE (future_set)); dex_unref (future); } } if (future_set->futures != future_set->embedded) g_clear_pointer (&future_set->futures, g_free); future_set->futures = NULL; future_set->n_futures = 0; future_set->flags = 0; future_set->n_resolved = 0; future_set->n_rejected = 0; DEX_OBJECT_CLASS (dex_future_set_parent_class)->finalize (object); } static void dex_future_set_class_init (DexFutureSetClass *future_set_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (future_set_class); DexFutureClass *future_class = DEX_FUTURE_CLASS (future_set_class); object_class->finalize = dex_future_set_finalize; future_class->propagate = dex_future_set_propagate; g_value_init (&success_value, G_TYPE_BOOLEAN); g_value_set_boolean (&success_value, TRUE); } static void dex_future_set_init (DexFutureSet *future_set) { } /** * dex_future_set_new: (skip) * @futures: (transfer none) (array length=n_futures): an array of futures to chain * @n_futures: number of futures * @flags: the flags for how the future set should resolve/reject * * Creates a new future set (also a future) which will resovle or reject * based on the completion from sub #DexFuture. * * There must be at least 1 future provided in @futures. * * Returns: the new #DexFutureSet */ DexFutureSet * dex_future_set_new (DexFuture * const *futures, guint n_futures, DexFutureSetFlags flags) { DexFutureSet *future_set; g_return_val_if_fail (futures != NULL, NULL); g_return_val_if_fail (n_futures > 0, NULL); g_return_val_if_fail ((flags & DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST) == 0 || (flags & (DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE|DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT)) != 0, NULL); /* Setup our new DexFuture to contain the results */ future_set = (DexFutureSet *)dex_object_create_instance (DEX_TYPE_FUTURE_SET); future_set->n_futures = n_futures; future_set->flags = flags; if (n_futures <= G_N_ELEMENTS (future_set->embedded)) future_set->futures = future_set->embedded; else future_set->futures = g_new0 (DexFuture *, n_futures); /* Ref all futures before potentially calling out to chain them */ for (guint i = 0; i < n_futures; i++) future_set->futures[i] = dex_ref (futures[i]); /* Now start chaining futures, even if we progress from pending while * we iterate this list (as we're safe against multiple resolves). */ for (guint i = 0; i < n_futures; i++) dex_future_chain (future_set->futures[i], DEX_FUTURE (future_set)); return future_set; } DexFutureSet * dex_future_set_new_va (DexFuture *first_future, va_list *args, DexFutureSetFlags flags) { DexFutureSet *future_set; DexFuture *future = first_future; guint capacity = 8; g_return_val_if_fail (DEX_IS_FUTURE (first_future), NULL); g_return_val_if_fail ((flags & DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST) == 0 || (flags & (DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE|DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT)) != 0, NULL); future_set = (DexFutureSet *)dex_object_create_instance (DEX_TYPE_FUTURE_SET); future_set->flags = flags; future_set->futures = future_set->embedded; g_assert (capacity > G_N_ELEMENTS (future_set->embedded)); while (future != NULL) { if G_UNLIKELY (future_set->n_futures == G_N_ELEMENTS (future_set->embedded)) { future_set->futures = g_new0 (DexFuture *, capacity); for (guint j = 0; j < G_N_ELEMENTS (future_set->embedded); j++) future_set->futures[j] = future_set->embedded[j]; } else if G_UNLIKELY (future_set->n_futures + 1 > capacity) { capacity *= 2; future_set->futures = g_realloc_n (future_set->futures, capacity, sizeof (DexFuture *)); } future_set->futures[future_set->n_futures++] = future; future = va_arg (*args, DexFuture *); } /* Now start chaining futures, even if we progress from pending while * we iterate this list (as we're safe against multiple resolves). */ for (guint i = 0; i < future_set->n_futures; i++) dex_future_chain (future_set->futures[i], DEX_FUTURE (future_set)); return future_set; } /** * dex_future_set_get_size: * @future_set: a #DexFutureSet * * Gets the number of futures associated with the #DexFutureSet. You may * use dex_future_set_get_future_at() to obtain the individual #DexFuture. * * Returns: the number of #DexFuture in @future_set. */ guint dex_future_set_get_size (DexFutureSet *future_set) { g_return_val_if_fail (DEX_IS_FUTURE_SET (future_set), 0); return future_set->n_futures; } /** * dex_future_set_get_future_at: * @future_set: a #DexFutureSet * * Gets a #DexFuture that was used to produce the result of @future_set. * * Use dex_future_set_get_size() to determine the number of #DexFuture that * are contained within the #DexFutureSet. * * Returns: (transfer none): a #DexFuture */ DexFuture * dex_future_set_get_future_at (DexFutureSet *future_set, guint position) { g_return_val_if_fail (DEX_IS_FUTURE_SET (future_set), NULL); g_return_val_if_fail (position < future_set->n_futures, NULL); return future_set->futures[position]; } /** * dex_future_set_get_value_at: * @future_set: a #DexFutureSet * @position: the #DexFuture position within the set * @error: location for a #GError, or %NULL * * Gets the result from a #DexFuture that is part of the * #DexFutureSet. * * Returns: (transfer none): a #GValue if successful; otherwise %NULL * and @error is set. */ const GValue * dex_future_set_get_value_at (DexFutureSet *future_set, guint position, GError **error) { g_return_val_if_fail (DEX_IS_FUTURE_SET (future_set), NULL); g_return_val_if_fail (position < future_set->n_futures, NULL); return dex_future_get_value (future_set->futures[position], error); } 0707010000003F000081A40000000000000000000000016712D33C000006FF000000000000000000000000000000000000002200000000libdex-0.8.1/src/dex-future-set.h/* * dex-future-set.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_FUTURE_SET (dex_future_set_get_type()) #define DEX_FUTURE_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_FUTURE_SET, DexFutureSet)) #define DEX_IS_FUTURE_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_FUTURE_SET)) typedef struct _DexFutureSet DexFutureSet; DEX_AVAILABLE_IN_ALL GType dex_future_set_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL guint dex_future_set_get_size (DexFutureSet *future_set); DEX_AVAILABLE_IN_ALL const GValue *dex_future_set_get_value_at (DexFutureSet *future_set, guint position, GError **error); DEX_AVAILABLE_IN_ALL DexFuture *dex_future_set_get_future_at (DexFutureSet *future_set, guint position); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexFutureSet, dex_unref) G_END_DECLS 07070100000040000081A40000000000000000000000016712D33C0000B33A000000000000000000000000000000000000001E00000000libdex-0.8.1/src/dex-future.c/* * dex-future.c * * Copyright 2022-2023 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gio/gio.h> #include "dex-block-private.h" #include "dex-error.h" #include "dex-future-private.h" #include "dex-future-set-private.h" #include "dex-infinite-private.h" #include "dex-promise.h" #include "dex-scheduler.h" #include "dex-static-future-private.h" /** * DexFuture: * * #DexFuture is the base class representing a future which may resolve with * a value or reject with error at some point in the future. * * It is the basis for libdex's concurrency and parallelism model. * * Use futures to represent work in progress and allow consumers to build * robust processing chains up front which will complete or fail as futures * resolve or reject. * * When running on a #DexFiber, you may use dex_await() and similar functions * to suspend the current thread and return upon completion of the dependent * future. */ static void dex_future_propagate (DexFuture *future, DexFuture *completed); DEX_DEFINE_DERIVABLE_TYPE (DexFuture, dex_future, DEX_TYPE_OBJECT) #undef DEX_TYPE_FUTURE #define DEX_TYPE_FUTURE dex_future_type static gsize static_booleans_init; static DexFuture *static_booleans[2]; typedef struct _DexChainedFuture { GList link; DexWeakRef wr; gpointer where_future_was; guint awaiting : 1; } DexChainedFuture; static DexChainedFuture * dex_chained_future_new (gpointer object) { DexChainedFuture *cf; cf = g_new0 (DexChainedFuture, 1); cf->link.data = cf; cf->awaiting = TRUE; cf->where_future_was = object; dex_weak_ref_init (&cf->wr, object); return cf; } static void dex_chained_future_free (DexChainedFuture *cf) { g_assert (cf != NULL); g_assert (cf->link.prev == NULL); g_assert (cf->link.next == NULL); g_assert (cf->link.data == cf); dex_weak_ref_clear (&cf->wr); cf->link.data = NULL; cf->where_future_was = NULL; cf->awaiting = FALSE; g_free (cf); } void dex_future_complete_from (DexFuture *future, DexFuture *completed) { GError *error = NULL; const GValue *value = dex_future_get_value (completed, &error); dex_future_complete (future, value, error); } void dex_future_complete (DexFuture *future, const GValue *resolved, GError *rejected) { GQueue queue = G_QUEUE_INIT; g_return_if_fail (DEX_IS_FUTURE (future)); g_return_if_fail (resolved != NULL || rejected != NULL); g_return_if_fail (resolved == NULL || G_IS_VALUE (resolved)); dex_object_lock (DEX_OBJECT (future)); if (future->status == DEX_FUTURE_STATUS_PENDING) { if (resolved != NULL) { g_value_init (&future->resolved, G_VALUE_TYPE (resolved)); g_value_copy (resolved, &future->resolved); future->status = DEX_FUTURE_STATUS_RESOLVED; } else { future->rejected = g_steal_pointer (&rejected); future->status = DEX_FUTURE_STATUS_REJECTED; } queue = future->chained; future->chained = (GQueue) {NULL, NULL, 0}; } else { g_clear_error (&rejected); } dex_object_unlock (DEX_OBJECT (future)); /* Iterate in reverse order to give some predictable ordering based on * when chained futures were attached. We've released the lock at this * point to avoid any requests back on future from deadlocking. */ while (queue.tail != NULL) { DexChainedFuture *cf = g_queue_pop_tail_link (&queue)->data; DexFuture *chained; g_assert (cf != NULL); g_assert (cf->link.data == cf); chained = dex_weak_ref_get (&cf->wr); dex_weak_ref_set (&cf->wr, NULL); g_assert (!chained || DEX_IS_FUTURE (chained)); /* Always notify even if the future isn't awaiting as * it can provide a bit more information to futures that * are bringing in results until their callbacks are * scheduled for execution. */ if (chained != NULL) { dex_future_propagate (chained, future); dex_unref (chained); } dex_chained_future_free (cf); } } const GValue * dex_future_get_value (DexFuture *future, GError **error) { const GValue *ret; g_return_val_if_fail (DEX_IS_FUTURE (future), FALSE); dex_object_lock (DEX_OBJECT (future)); switch (future->status) { case DEX_FUTURE_STATUS_PENDING: g_set_error_literal (error, DEX_ERROR, DEX_ERROR_PENDING, "Future is still pending"); ret = NULL; break; case DEX_FUTURE_STATUS_RESOLVED: ret = &future->resolved; break; case DEX_FUTURE_STATUS_REJECTED: ret = NULL; if (error != NULL) *error = g_error_copy (future->rejected); break; default: g_assert_not_reached (); } dex_object_unlock (DEX_OBJECT (future)); return ret; } DexFutureStatus dex_future_get_status (DexFuture *future) { DexFutureStatus status; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); dex_object_lock (DEX_OBJECT (future)); status = future->status; dex_object_unlock (DEX_OBJECT (future)); return status; } /** * dex_future_is_resolved: * @future: a #DexFuture * * This is a convenience function equivalent to calling * dex_future_get_status() and checking for %DEX_FUTURE_STATUS_RESOLVED. * * Returns: %TRUE if the future has successfully resolved with a value; * otherwise %FALSE */ gboolean dex_future_is_resolved (DexFuture *future) { gboolean ret; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); dex_object_lock (DEX_OBJECT (future)); ret = future->status == DEX_FUTURE_STATUS_RESOLVED; dex_object_unlock (DEX_OBJECT (future)); return ret; } /** * dex_future_is_rejected: * @future: a #DexFuture * * This is a convenience function equivalent to calling * dex_future_get_status() and checking for %DEX_FUTURE_STATUS_REJECTED. * * Returns: %TRUE if the future was rejected with an error; otherwise %FALSE */ gboolean dex_future_is_rejected (DexFuture *future) { gboolean ret; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); dex_object_lock (DEX_OBJECT (future)); ret = future->status == DEX_FUTURE_STATUS_REJECTED; dex_object_unlock (DEX_OBJECT (future)); return ret; } /** * dex_future_is_pending: * @future: a #DexFuture * * This is a convenience function equivalent to calling * dex_future_get_status() and checking for %DEX_FUTURE_STATUS_PENDING. * * Returns: %TRUE if the future is still pending; otherwise %FALSE */ gboolean dex_future_is_pending (DexFuture *future) { gboolean ret; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); dex_object_lock (DEX_OBJECT (future)); ret = future->status == DEX_FUTURE_STATUS_PENDING; dex_object_unlock (DEX_OBJECT (future)); return ret; } static void dex_future_finalize (DexObject *object) { DexFuture *future = (DexFuture *)object; g_assert (future->chained.length == 0); g_assert (future->chained.head == NULL); g_assert (future->chained.tail == NULL); if (G_IS_VALUE (&future->resolved)) g_value_unset (&future->resolved); g_clear_error (&future->rejected); DEX_OBJECT_CLASS (dex_future_parent_class)->finalize (object); } static void dex_future_class_init (DexFutureClass *klass) { DexObjectClass *object_class = DEX_OBJECT_CLASS (klass); object_class->finalize = dex_future_finalize; } static void dex_future_init (DexFuture *future) { } static void dex_future_propagate (DexFuture *future, DexFuture *completed) { gboolean handled; g_return_if_fail (DEX_IS_FUTURE (future)); g_return_if_fail (DEX_IS_FUTURE (completed)); dex_ref (completed); if (DEX_FUTURE_GET_CLASS (future)->propagate) handled = DEX_FUTURE_GET_CLASS (future)->propagate (future, completed); else handled = FALSE; if (!handled) { const GValue *resolved = NULL; const GError *rejected = NULL; dex_object_lock (future); if (completed->status == DEX_FUTURE_STATUS_RESOLVED) resolved = &completed->resolved; else rejected = completed->rejected; dex_object_unlock (future); dex_future_complete (future, resolved, rejected ? g_error_copy (rejected) : NULL); } dex_unref (completed); } void dex_future_chain (DexFuture *future, DexFuture *chained) { gboolean did_chain = FALSE; g_return_if_fail (DEX_IS_FUTURE (future)); g_return_if_fail (DEX_IS_FUTURE (chained)); dex_object_lock (future); if (future->status == DEX_FUTURE_STATUS_PENDING) { DexChainedFuture *cf = dex_chained_future_new (chained); g_queue_push_tail_link (&future->chained, &cf->link); did_chain = TRUE; } dex_object_unlock (future); if (!did_chain) dex_future_propagate (chained, future); } void dex_future_discard (DexFuture *future, DexFuture *chained) { gboolean has_awaiting = FALSE; gboolean matched = FALSE; GQueue discarded = G_QUEUE_INIT; g_return_if_fail (DEX_IS_FUTURE (future)); g_return_if_fail (DEX_IS_FUTURE (chained)); dex_object_lock (future); #if 0 /* Mark the chained future as no longer necessary to dispatch to. * If so, we can possibly request that @future discard any ongoing * operations so that we propagate cancellation. * * However, if the status has already completed, just short-circuit. */ if (future->status != DEX_FUTURE_STATUS_PENDING) { dex_object_unlock (future); return; } #endif for (const GList *iter = future->chained.head; iter; ) { DexChainedFuture *cf = iter->data; iter = iter->next; if (chained == cf->where_future_was) { if (cf->awaiting == TRUE) { matched = TRUE; cf->awaiting = FALSE; } has_awaiting |= cf->awaiting; g_queue_unlink (&future->chained, &cf->link); g_queue_push_tail_link (&discarded, &cf->link); } } dex_object_unlock (future); /* Release chained futures outside of the @future lock */ while (discarded.head != NULL) { DexChainedFuture *cf = discarded.head->data; g_queue_unlink (&discarded, &cf->link); dex_chained_future_free (cf); } /* If we discarded the chained future and there are no more futures * awaiting our response, then request the class discard the future, * possibly cancelling anything in flight. */ if (matched && !has_awaiting) { if (DEX_FUTURE_GET_CLASS (future)->discard) { dex_ref (future); DEX_FUTURE_GET_CLASS (future)->discard (future); dex_unref (future); } } } /** * dex_future_then: (constructor) * @future: (transfer full): a #DexFuture * @callback: (scope notified): a callback to execute * @callback_data: closure data for @callback * @callback_data_destroy: destroy notify for @callback_data * * Calls @callback when @future resolves. * * If @future rejects, then @callback will not be called. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_then) (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) { g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); g_return_val_if_fail (callback != NULL, NULL); return dex_block_new (future, NULL, DEX_BLOCK_KIND_THEN, callback, callback_data, callback_data_destroy); } /** * dex_future_then_loop: (constructor) * @future: (transfer full): a #DexFuture * @callback: (scope notified) (closure callback_data) (destroy callback_data_destroy): a callback to execute * @callback_data: closure data for @callback * @callback_data_destroy: destroy notify for @callback_data * * Asynchronously calls @callback when @future resolves. * * This is similar to dex_future_then() except that it will call * @callback multiple times as each returned #DexFuture resolves or * rejects, allowing for infinite loops. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_then_loop) (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) { g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); g_return_val_if_fail (callback != NULL, NULL); return dex_block_new (future, NULL, DEX_BLOCK_KIND_THEN | DEX_BLOCK_KIND_LOOP, callback, callback_data, callback_data_destroy); } /** * dex_future_catch_loop: (constructor) * @future: (transfer full): a #DexFuture * @callback: (scope notified) (closure callback_data) (destroy callback_data_destroy): a callback to execute * @callback_data: closure data for @callback * @callback_data_destroy: destroy notify for @callback_data * * Asynchronously calls @callback when @future rejects. * * This is similar to dex_future_catch() except that it will call * @callback multiple times as each returned #DexFuture rejects, * allowing for infinite loops. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_catch_loop) (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) { g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); g_return_val_if_fail (callback != NULL, NULL); return dex_block_new (future, NULL, DEX_BLOCK_KIND_CATCH | DEX_BLOCK_KIND_LOOP, callback, callback_data, callback_data_destroy); } /** * dex_future_finally_loop: (constructor) * @future: (transfer full): a #DexFuture * @callback: (scope notified) (closure callback_data) (destroy callback_data_destroy): a callback to execute * @callback_data: closure data for @callback * @callback_data_destroy: destroy notify for @callback_data * * Asynchronously calls @callback when @future rejects or resolves. * * This is similar to dex_future_finally() except that it will call * @callback multiple times as each returned #DexFuture rejects or resolves, * allowing for infinite loops. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_finally_loop) (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) { g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); g_return_val_if_fail (callback != NULL, NULL); return dex_block_new (future, NULL, DEX_BLOCK_KIND_THEN | DEX_BLOCK_KIND_CATCH | DEX_BLOCK_KIND_LOOP, callback, callback_data, callback_data_destroy); } /** * dex_future_catch: (constructor) * @future: (transfer full): a #DexFuture * @callback: (scope notified) (closure callback_data) (destroy callback_data_destroy): a callback to execute * @callback_data: closure data for @callback * @callback_data_destroy: destroy notify for @callback_data * * Calls @callback when @future rejects. * * If @future resolves, then @callback will not be called. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_catch) (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) { g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); g_return_val_if_fail (callback != NULL, NULL); return dex_block_new (future, NULL, DEX_BLOCK_KIND_CATCH, callback, callback_data, callback_data_destroy); } /** * dex_future_finally: (constructor) * @future: (transfer full): a #DexFuture * @callback: (scope notified) (closure callback_data) (destroy callback_data_destroy): a callback to execute * @callback_data: closure data for @callback * @callback_data_destroy: destroy notify for @callback_data * * Calls @callback when @future resolves or rejects. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_finally) (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) { g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); g_return_val_if_fail (callback != NULL, NULL); return dex_block_new (future, NULL, DEX_BLOCK_KIND_FINALLY, callback, callback_data, callback_data_destroy); } /** * dex_future_all: (constructor) * @first_future: (transfer full): a #DexFuture * @...: a %NULL terminated list of futures * * Creates a new #DexFuture that will resolve or reject when all futures * either resolve or reject. * * This method will return a #DexFutureSet which provides API to get * the exact values of the dependent futures. The value of the future * if resolved will be a %G_TYPE_BOOLEAN of %TRUE. * * Returns: (transfer full) (type DexFuture): a #DexFutureSet */ DexFuture * (dex_future_all) (DexFuture *first_future, ...) { DexFutureSet *ret; va_list args; va_start (args, first_future); ret = dex_future_set_new_va (first_future, &args, DEX_FUTURE_SET_FLAGS_NONE); va_end (args); return DEX_FUTURE (ret); } /** * dex_future_any: (constructor) * @first_future: (transfer full): a #DexFuture * @...: a %NULL terminated list of futures * * Creates a new #DexFuture that will resolve when any dependent future * resolves, providing the same result as the resolved future. * * If no futures resolve, then the future will reject. * * Returns: (transfer full) (type DexFuture): a #DexFutureSet */ DexFuture * (dex_future_any) (DexFuture *first_future, ...) { DexFutureSet *ret; va_list args; va_start (args, first_future); ret = dex_future_set_new_va (first_future, &args, (DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST | DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE)); va_end (args); return DEX_FUTURE (ret); } /** * dex_future_all_race: (constructor) * @first_future: (transfer full): a #DexFuture * @...: a %NULL terminated list of futures * * Creates a new #DexFuture that will resolve when all futures resolve * or reject as soon as the first future rejects. * * This method will return a #DexFutureSet which provides API to get * the exact values of the dependent futures. The value of the future * will be propagated from the resolved or rejected future. * * Since the futures race to complete, some futures retrieved with the * dex_future_set_get_future() API will still be %DEX_FUTURE_STATUS_PENDING. * * Returns: (transfer full) (type DexFuture): a #DexFutureSet */ DexFuture * (dex_future_all_race) (DexFuture *first_future, ...) { DexFutureSet *ret; va_list args; va_start (args, first_future); ret = dex_future_set_new_va (first_future, &args, (DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST | DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT)); va_end (args); return DEX_FUTURE (ret); } /** * dex_future_first: (constructor) * @first_future: (transfer full): a #DexFuture * @...: a %NULL terminated list of futures * * Creates a new #DexFuture that resolves or rejects as soon as the * first dependent future resolves or rejects, sharing the same result. * * Returns: (transfer full) (type DexFuture): a #DexFutureSet */ DexFuture * (dex_future_first) (DexFuture *first_future, ...) { DexFutureSet *ret; va_list args; va_start (args, first_future); ret = dex_future_set_new_va (first_future, &args, (DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST | DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE | DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT)); va_end (args); return DEX_FUTURE (ret); } /** * dex_future_firstv: (rename-to dex_future_first) (constructor) * @futures: (array length=n_futures) (transfer none): an array of futures * @n_futures: the number of futures * * Creates a new #DexFuture that resolves or rejects as soon as the * first dependent future resolves or rejects, sharing the same result. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_firstv) (DexFuture * const *futures, guint n_futures) { return DEX_FUTURE (dex_future_set_new (futures, n_futures, (DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST | DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE | DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT))); } /** * dex_future_anyv: (rename-to dex_future_any) (constructor) * @futures: (array length=n_futures) (transfer none): an array of futures * @n_futures: the number of futures * * Creates a new #DexFuture that resolves when the first future resolves. * * If all futures reject, then the #DexFuture returned will also reject. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_anyv) (DexFuture * const *futures, guint n_futures) { return DEX_FUTURE (dex_future_set_new (futures, n_futures, (DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST | DEX_FUTURE_SET_FLAGS_PROPAGATE_RESOLVE))); } /** * dex_future_all_racev: (rename-to dex_future_all_race) (constructor) * @futures: (array length=n_futures) (transfer none): an array of futures * @n_futures: the number of futures * * Creates a new #DexFuture that resolves when all futures resolve. * * If any future rejects, the resulting #DexFuture also rejects immediately. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_all_racev) (DexFuture * const *futures, guint n_futures) { return DEX_FUTURE (dex_future_set_new (futures, n_futures, (DEX_FUTURE_SET_FLAGS_PROPAGATE_FIRST | DEX_FUTURE_SET_FLAGS_PROPAGATE_REJECT))); } /** * dex_future_allv: (rename-to dex_future_all) (constructor) * @futures: (array length=n_futures) (transfer none): an array of futures * @n_futures: the number of futures * * Creates a new #DexFuture that resolves when all futures resolve. * * The resulting #DexFuture will not resolve or reject until all futures * have either resolved or rejected. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_allv) (DexFuture * const *futures, guint n_futures) { return DEX_FUTURE (dex_future_set_new (futures, n_futures, DEX_FUTURE_SET_FLAGS_NONE)); } /** * dex_future_set_static_name: (skip) * @future: a #DexFuture * @name: the name of the future * * Sets the name of the future with a static/internal string. * * @name will not be copied, so it must be static/internal which can be done * either by using string literals or by using g_string_intern(). */ void dex_future_set_static_name (DexFuture *future, const char *name) { g_return_if_fail (DEX_IS_FUTURE (future)); dex_object_lock (future); future->name = name; dex_object_unlock (future); } const char * dex_future_get_name (DexFuture *future) { const char *name; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); dex_object_lock (future); name = future->name; dex_object_unlock (future); return name; } /** * dex_future_new_for_value: * @value: the resolved #GValue * * Creates a read-only #DexFuture that has resolved. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_new_for_value) (const GValue *value) { g_return_val_if_fail (G_IS_VALUE (value), NULL); return dex_static_future_new_resolved (value); } /** * dex_future_new_for_error: (constructor) * @error: (transfer full): a #GError * * Creates a read-only #DexFuture that has rejected. * * Returns: (transfer full): a #DexFuture */ DexFuture * (dex_future_new_for_error) (GError *error) { g_return_val_if_fail (error != NULL, NULL); return dex_static_future_new_rejected (error); } /** * dex_future_new_for_boolean: (constructor) * @v_bool: the resolved value for the future * * Creates a new #DexFuture and resolves it with @v_bool. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_boolean) (gboolean v_bool) { if G_UNLIKELY (g_once_init_enter (&static_booleans_init)) { GValue value = G_VALUE_INIT; g_value_init (&value, G_TYPE_BOOLEAN); g_value_set_boolean (&value, FALSE); static_booleans[FALSE] = dex_static_future_new_resolved (&value); g_value_set_boolean (&value, TRUE); static_booleans[TRUE] = dex_static_future_new_resolved (&value); g_once_init_leave (&static_booleans_init, TRUE); } return dex_ref (static_booleans[!!v_bool]); } /** * dex_future_new_for_int: (constructor) * @v_int: the resolved value for the future * * Creates a new #DexFuture and resolves it with @v_int. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_int) (int v_int) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, v_int); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_for_int64: (constructor) * @v_int64: the resolved value for the future * * Creates a new #DexFuture and resolves it with @v_int64. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_int64) (gint64 v_int64) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_INT64); g_value_set_int64 (&value, v_int64); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_for_uint64: (constructor) * @v_uint64: the resolved value for the future * * Creates a new #DexFuture and resolves it with @v_uint64. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_uint64) (guint64 v_uint64) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_UINT64); g_value_set_uint64 (&value, v_uint64); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_for_float: (constructor) * @v_float: the resolved value for the future * * Creates a new #DexFuture and resolves it with @v_float. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_float) (gfloat v_float) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_FLOAT); g_value_set_float (&value, v_float); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_for_double: (constructor) * @v_double: the resolved value for the future * * Creates a new #DexFuture and resolves it with @v_double. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_double) (gdouble v_double) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_DOUBLE); g_value_set_double (&value, v_double); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_for_uint: (constructor) * @v_uint: the resolved value for the future * * Creates a new #DexFuture and resolves it with @v_uint. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_uint) (guint v_uint) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, v_uint); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_for_string: (constructor) * @string: the resolved value for the future * * Creates a new #DexFuture and resolves it with @string. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_string) (const char *string) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_STRING); g_value_set_static_string (&value, string); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_take_string: (constructor) * @string: (transfer full): the resolved value for the future * * Creates a new #DexFuture and resolves it with @string. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_take_string) (char *string) { GValue value = G_VALUE_INIT; DexFuture *future; g_value_init (&value, G_TYPE_STRING); g_value_take_string (&value, string); future = (dex_future_new_for_value) (&value); g_value_unset (&value); return future; } /** * dex_future_new_take_boxed: (constructor) (skip) * @boxed_type: the GBoxed-based type * @value: (transfer full): the value for the boxed type * * Creates a new #DexFuture that is resolved with @value. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_take_boxed) (GType boxed_type, gpointer value) { GValue gvalue = G_VALUE_INIT; DexFuture *ret; g_return_val_if_fail (G_TYPE_FUNDAMENTAL (boxed_type) == G_TYPE_BOXED, NULL); g_value_init (&gvalue, boxed_type); g_value_take_boxed (&gvalue, value); ret = dex_future_new_for_value (&gvalue); g_value_unset (&gvalue); return ret; } /** * dex_future_new_take_variant: (constructor) (skip) * @v_variant: (transfer full): the variant to take ownership of * * Creates a new #DexFuture that is resolved with @v_variant. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_take_variant) (GVariant *v_variant) { GValue gvalue = G_VALUE_INIT; DexFuture *ret; g_value_init (&gvalue, G_TYPE_VARIANT); g_value_take_variant (&gvalue, v_variant); ret = dex_future_new_for_value (&gvalue); g_value_unset (&gvalue); return ret; } /** * dex_future_new_for_pointer: (constructor) * @pointer: the resolved future value as a pointer * * Creates a new #DexFuture that is resolved with @pointer as a %G_TYPE_POINTER. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_pointer) (gpointer pointer) { GValue gvalue = G_VALUE_INIT; DexFuture *ret; g_value_init (&gvalue, G_TYPE_POINTER); g_value_set_pointer (&gvalue, pointer); ret = dex_future_new_for_value (&gvalue); g_value_unset (&gvalue); return ret; } /** * dex_future_new_for_object: (constructor) * @value: (type GObject): the value * * Creates a new #DexFuture that is resolved with @value. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_for_object) (gpointer value) { GValue gvalue = G_VALUE_INIT; DexFuture *ret; g_return_val_if_fail (G_IS_OBJECT (value), NULL); g_value_init (&gvalue, G_OBJECT_TYPE (value)); g_value_set_object (&gvalue, value); ret = dex_future_new_for_value (&gvalue); g_value_unset (&gvalue); return ret; } /** * dex_future_new_take_object: (constructor) * @value: (transfer full) (type GObject) (nullable): the value * * Creates a new #DexFuture that is resolved with @value. * * Returns: (transfer full): a resolved #DexFuture */ DexFuture * (dex_future_new_take_object) (gpointer value) { GValue gvalue = G_VALUE_INIT; DexFuture *ret; g_return_val_if_fail (!value || G_IS_OBJECT (value), NULL); g_value_init (&gvalue, value ? G_OBJECT_TYPE (value) : G_TYPE_OBJECT); g_value_take_object (&gvalue, value); ret = dex_future_new_for_value (&gvalue); g_value_unset (&gvalue); return ret; } /** * dex_future_new_reject: (constructor) * @domain: the error domain * @error_code: the error code * @format: a printf-style format string * * Creates a new #DexFuture that is rejeced. * * Returns: (transfer full): a new #DexFuture */ DexFuture * (dex_future_new_reject) (GQuark domain, int error_code, const char *format, ...) { GError *error; va_list args; va_start (args, format); error = g_error_new_valist (domain, error_code, format, args); va_end (args); g_return_val_if_fail (error != NULL, NULL); return dex_future_new_for_error (error); } /** * dex_future_new_for_errno: * @errno_: the `errno` to use for rejection * * Creates a new rejected future using @errno_ as the value * of errno for the GError. * * The resulting error domain will be %G_IO_ERROR. * * Returns: (transfer full): a rejected #DexFuture. * * Since: 0.4 */ DexFuture * (dex_future_new_for_errno) (int errno_) { /* NOTE: We might be able to cache some common rejections * by errno to avoid re-creating them. */ return dex_future_new_for_error (g_error_new_literal (G_IO_ERROR, g_io_error_from_errno (errno_), g_strerror (errno_))); } /** * dex_future_new_infinite: * * Creates an infinite future that will never resolve or reject. This can * be useful when you want to mock a situation of "run forever" unless * another future rejects or resolves. * * Returns: (transfer full): a #DexFuture that will never complete or reject * * Since: 0.4 */ DexFuture * dex_future_new_infinite (void) { return dex_infinite_new (); } static const GValue * dex_await_check (DexFuture *future, GType type, GError **error) { const GValue *value; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); if ((value = dex_await_borrowed (future, error))) { if (!G_VALUE_HOLDS (value, type)) { g_set_error (error, DEX_ERROR, DEX_ERROR_TYPE_MISMATCH, "Got type %s, expected %s", G_VALUE_TYPE_NAME (value), g_type_name (type)); return NULL; } } return value; } /** * dex_await_pointer: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Calls dex_await() and returns the value of g_value_get_pointer(), * otherwise @error is set if the future rejected. * * Returns: (nullable): a pointer or %NULL */ gpointer dex_await_pointer (DexFuture *future, GError **error) { const GValue *value; gpointer ret = NULL; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); if ((value = dex_await_check (future, G_TYPE_POINTER, error))) ret = g_value_get_pointer (value); dex_unref (future); return ret; } /** * dex_await_int: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the result as an int. * * The resolved value must be of type %G_TYPE_INT or @error is set. * * Returns: an int, or 0 in case of failure and @error is set. */ int dex_await_int (DexFuture *future, GError **error) { const GValue *value; int ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_INT, error))) ret = g_value_get_int (value); dex_unref (future); return ret; } /** * dex_await_uint: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the result as an uint. * * The resolved value must be of type %G_TYPE_UINT or @error is set. * * Returns: an uint, or 0 in case of failure and @error is set. */ guint dex_await_uint (DexFuture *future, GError **error) { const GValue *value; guint ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_UINT, error))) ret = g_value_get_uint (value); dex_unref (future); return ret; } /** * dex_await_int64: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the result as an int64. * * The resolved value must be of type %G_TYPE_INT64 or @error is set. * * Returns: an int64, or 0 in case of failure and @error is set. */ gint64 dex_await_int64 (DexFuture *future, GError **error) { const GValue *value; gint64 ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_INT64, error))) ret = g_value_get_int64 (value); dex_unref (future); return ret; } /** * dex_await_uint64: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the result as an uint64. * * The resolved value must be of type %G_TYPE_UINT64 or @error is set. * * Returns: an uint64, or 0 in case of failure and @error is set. */ guint64 dex_await_uint64 (DexFuture *future, GError **error) { const GValue *value; guint64 ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_UINT64, error))) ret = g_value_get_uint64 (value); dex_unref (future); return ret; } /** * dex_await_double: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the result as an double. * * The resolved value must be of type %G_TYPE_DOUBLE or @error is set. * * Returns: an double, or 0 in case of failure and @error is set. */ double dex_await_double (DexFuture *future, GError **error) { const GValue *value; double ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_DOUBLE, error))) ret = g_value_get_double (value); dex_unref (future); return ret; } /** * dex_await_float: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the result as an float. * * The resolved value must be of type %G_TYPE_FLOAT or @error is set. * * Returns: an float, or 0 in case of failure and @error is set. */ float dex_await_float (DexFuture *future, GError **error) { const GValue *value; float ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_FLOAT, error))) ret = g_value_get_float (value); dex_unref (future); return ret; } /** * dex_await_boxed: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the %G_TYPE_BOXED based result. * * Returns: (transfer full): the boxed result, or %NULL and @error is set. */ gpointer dex_await_boxed (DexFuture *future, GError **error) { const GValue *value; gpointer ret = NULL; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_BOXED, error))) ret = g_value_dup_boxed (value); dex_unref (future); return ret; } /** * dex_await_variant: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the %G_TYPE_VARIANT based result. * * Returns: (transfer full): the variant result, or %NULL and @error is set. * * Since: 0.4 */ GVariant * dex_await_variant (DexFuture *future, GError **error) { const GValue *value; GVariant *ret = NULL; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_VARIANT, error))) ret = g_value_dup_variant (value); dex_unref (future); return ret; } /** * dex_await_object: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the #GObject-based result. * * Returns: (type GObject) (transfer full): the object, or %NULL and @error is set. */ gpointer dex_await_object (DexFuture *future, GError **error) { const GValue *value; gpointer ret = NULL; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); if ((value = dex_await_check (future, G_TYPE_OBJECT, error))) ret = g_value_dup_object (value); dex_unref (future); return ret; } /** * dex_await_boolean: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the gboolean result. * * If the result is not a #gboolean, @error is set. * * Returns: the #gboolean, or %FALSE and @error is set */ gboolean dex_await_boolean (DexFuture *future, GError **error) { const GValue *value; gboolean ret = FALSE; g_return_val_if_fail (DEX_IS_FUTURE (future), FALSE); if ((value = dex_await_check (future, G_TYPE_BOOLEAN, error))) ret = g_value_get_boolean (value); dex_unref (future); return ret; } /** * dex_await_string: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the string result. * * If the result is not a %G_TYPE_STRING, @error is set. * * Returns: (transfer full) (nullable): the string or %NULL and @error is set */ char * dex_await_string (DexFuture *future, GError **error) { const GValue *value; char *ret = NULL; g_return_val_if_fail (DEX_IS_FUTURE (future), NULL); if ((value = dex_await_check (future, G_TYPE_STRING, error))) ret = g_value_dup_string (value); dex_unref (future); return ret; } /** * dex_await_enum: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the enum result. * * If the result is not a %G_TYPE_ENUM, @error is set. * * Returns: the enum or 0 and @error is set. */ guint dex_await_enum (DexFuture *future, GError **error) { const GValue *value; guint ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_ENUM, error))) ret = g_value_get_enum (value); dex_unref (future); return ret; } /** * dex_await_flags: (method) * @future: (transfer full): a #DexFuture * @error: a location for a #GError * * Awaits on @future and returns the flags result. * * If the result is not a %G_TYPE_FLAGS, @error is set. * * Returns: the flags or 0 and @error is set. */ guint dex_await_flags (DexFuture *future, GError **error) { const GValue *value; guint ret = 0; g_return_val_if_fail (DEX_IS_FUTURE (future), 0); if ((value = dex_await_check (future, G_TYPE_FLAGS, error))) ret = g_value_get_flags (value); dex_unref (future); return ret; } static DexFuture ** pfuture_ref (DexFuture **pfuture) { return g_atomic_rc_box_acquire (pfuture); } static void pfuture_finalize (DexFuture **pfuture) { g_assert (pfuture != NULL); g_assert (*pfuture != NULL); g_assert (DEX_IS_FUTURE (*pfuture)); dex_unref (*pfuture); } static void pfuture_unref (DexFuture **pfuture) { g_atomic_rc_box_release_full (pfuture, (GDestroyNotify)pfuture_finalize); } static DexFuture * dex_future_disown_cb (DexFuture *resolved, gpointer user_data) { return NULL; } /** * dex_future_disown: * @future: (transfer full): a #DexFuture * * Disowns a future, allowing it to run to completion even though there may * be no observer interested in the futures completion or rejection. * * Since: 0.4 */ void dex_future_disown (DexFuture *future) { DexFuture **pfuture; g_return_if_fail (DEX_IS_FUTURE (future)); pfuture = g_atomic_rc_box_new0 (DexFuture *); *pfuture = dex_future_finally (future, dex_future_disown_cb, pfuture_ref (pfuture), (GDestroyNotify)pfuture_unref); pfuture_unref (pfuture); } 07070100000041000081A40000000000000000000000016712D33C00003AF1000000000000000000000000000000000000001E00000000libdex-0.8.1/src/dex-future.h/* * dex-future.h * * Copyright 2022-2023 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined (DEX_INSIDE) && !defined (DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif #include "dex-enums.h" #include "dex-object.h" G_BEGIN_DECLS #define DEX_TYPE_FUTURE (dex_future_get_type()) #define DEX_FUTURE(object) (G_TYPE_CHECK_INSTANCE_CAST(object, DEX_TYPE_FUTURE, DexFuture)) #define DEX_IS_FUTURE(object) (G_TYPE_CHECK_INSTANCE_TYPE(object, DEX_TYPE_FUTURE)) typedef struct _DexFuture DexFuture; /** * DexFutureCallback: * @future: a resolved or rejected #DexFuture * @user_data: closure data associated with the callback * * A #DexFutureCallback can be executed from a #DexBlock as response to * another #DexFuture resolving or rejecting. * * The callback will be executed within the scheduler environment the * block is created within when using dex_future_then(), dex_future_catch(), * dex_future_finally(), dex_future_all(), and similar functions. * * This is the expected way to handle completion of a future when not using * #DexFiber via dex_scheduler_spawn(). * * Returns: (transfer full) (nullable): a #DexFuture or %NULL */ typedef DexFuture *(*DexFutureCallback) (DexFuture *future, gpointer user_data); DEX_AVAILABLE_IN_ALL GType dex_future_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexFutureStatus dex_future_get_status (DexFuture *future); DEX_AVAILABLE_IN_ALL gboolean dex_future_is_resolved (DexFuture *future); DEX_AVAILABLE_IN_ALL gboolean dex_future_is_rejected (DexFuture *future); DEX_AVAILABLE_IN_ALL gboolean dex_future_is_pending (DexFuture *future); DEX_AVAILABLE_IN_ALL const GValue *dex_future_get_value (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_value (const GValue *value) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_error (GError *error) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_infinite (void) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_reject (GQuark domain, int error_code, const char *format, ...) G_GNUC_PRINTF (3, 4) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_errno (int errno_) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_string (const char *string) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_take_string (char *string) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_int (int v_int) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_uint (guint v_uint) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_int64 (gint64 v_int64) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_uint64 (guint64 v_uint64) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_boolean (gboolean v_bool) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_double (double v_double) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_float (float v_float) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_take_variant(GVariant *v_variant) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_take_boxed (GType boxed_type, gpointer value) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_pointer (gpointer pointer) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_for_object (gpointer value) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_new_take_object (gpointer value) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_then (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_then_loop (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_catch (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_catch_loop (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_finally (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_finally_loop (DexFuture *future, DexFutureCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_all (DexFuture *first_future, ...) G_GNUC_NULL_TERMINATED G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_allv (DexFuture * const *futures, guint n_futures) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_all_race (DexFuture *first_future, ...) G_GNUC_NULL_TERMINATED G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_all_racev (DexFuture * const *futures, guint n_futures) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_any (DexFuture *first_future, ...) G_GNUC_NULL_TERMINATED G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_anyv (DexFuture * const *futures, guint n_futures) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_first (DexFuture *first_future, ...) G_GNUC_NULL_TERMINATED G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_future_firstv (DexFuture * const *futures, guint n_futures) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL const char *dex_future_get_name (DexFuture *future); DEX_AVAILABLE_IN_ALL void dex_future_set_static_name (DexFuture *future, const char *name); DEX_AVAILABLE_IN_ALL void dex_future_disown (DexFuture *future); DEX_AVAILABLE_IN_ALL gboolean dex_await (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL gboolean dex_await_boolean (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL guint dex_await_enum (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL guint dex_await_flags (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL char *dex_await_string (DexFuture *future, GError **error) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL float dex_await_float (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL double dex_await_double (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL gpointer dex_await_pointer (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL int dex_await_int (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL guint dex_await_uint (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL gint64 dex_await_int64 (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL guint64 dex_await_uint64 (DexFuture *future, GError **error); DEX_AVAILABLE_IN_ALL GVariant *dex_await_variant (DexFuture *future, GError **error) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL gpointer dex_await_boxed (DexFuture *future, GError **error) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL gpointer dex_await_object (DexFuture *future, GError **error) G_GNUC_WARN_UNUSED_RESULT; #if G_GNUC_CHECK_VERSION(3,0) && defined(DEX_ENABLE_DEBUG) # define _DEX_FUTURE_NEW_(func, counter, ...) \ ({ DexFuture *G_PASTE(__f, counter) = G_PASTE (dex_future_, func) (__VA_ARGS__); \ dex_future_set_static_name (DEX_FUTURE (G_PASTE (__f, counter)), G_STRLOC); \ G_PASTE (__f, counter); }) # define _DEX_FUTURE_NEW(func, ...) _DEX_FUTURE_NEW_(func, __COUNTER__, __VA_ARGS__) # define dex_future_then(...) _DEX_FUTURE_NEW(then, __VA_ARGS__) # define dex_future_then_loop(...) _DEX_FUTURE_NEW(then_loop, __VA_ARGS__) # define dex_future_catch(...) _DEX_FUTURE_NEW(catch, __VA_ARGS__) # define dex_future_catch_loop(...) _DEX_FUTURE_NEW(catch_loop, __VA_ARGS__) # define dex_future_finally(...) _DEX_FUTURE_NEW(finally, __VA_ARGS__) # define dex_future_finally_loop(...) _DEX_FUTURE_NEW(finally_loop, __VA_ARGS__) # define dex_future_all(...) _DEX_FUTURE_NEW(all, __VA_ARGS__) # define dex_future_allv(...) _DEX_FUTURE_NEW(allv, __VA_ARGS__) # define dex_future_all_race(...) _DEX_FUTURE_NEW(all_race, __VA_ARGS__) # define dex_future_all_racev(...) _DEX_FUTURE_NEW(all_racev, __VA_ARGS__) # define dex_future_any(...) _DEX_FUTURE_NEW(any, __VA_ARGS__) # define dex_future_anyv(...) _DEX_FUTURE_NEW(anyv, __VA_ARGS__) # define dex_future_first(...) _DEX_FUTURE_NEW(first, __VA_ARGS__) # define dex_future_firstv(...) _DEX_FUTURE_NEW(firstv, __VA_ARGS__) # define dex_future_new_for_value(...) _DEX_FUTURE_NEW(new_for_value, __VA_ARGS__) # define dex_future_new_for_error(...) _DEX_FUTURE_NEW(new_for_error, __VA_ARGS__) # define dex_future_new_for_errno(...) _DEX_FUTURE_NEW(new_for_errno, __VA_ARGS__) # define dex_future_new_for_string(...) _DEX_FUTURE_NEW(new_for_string, __VA_ARGS__) # define dex_future_new_take_string(...) _DEX_FUTURE_NEW(new_take_string, __VA_ARGS__) # define dex_future_new_for_int(...) _DEX_FUTURE_NEW(new_for_int, __VA_ARGS__) # define dex_future_new_for_uint(...) _DEX_FUTURE_NEW(new_for_uint, __VA_ARGS__) # define dex_future_new_for_int64(...) _DEX_FUTURE_NEW(new_for_int64, __VA_ARGS__) # define dex_future_new_for_uint64(...) _DEX_FUTURE_NEW(new_for_uint64, __VA_ARGS__) # define dex_future_new_for_boolean(...) _DEX_FUTURE_NEW(new_for_boolean, __VA_ARGS__) # define dex_future_new_for_double(...) _DEX_FUTURE_NEW(new_for_double, __VA_ARGS__) # define dex_future_new_for_float(...) _DEX_FUTURE_NEW(new_for_float, __VA_ARGS__) # define dex_future_new_take_variant(...) _DEX_FUTURE_NEW(new_take_variant, __VA_ARGS__) # define dex_future_new_take_boxed(...) _DEX_FUTURE_NEW(new_take_boxed, __VA_ARGS__) # define dex_future_new_for_object(...) _DEX_FUTURE_NEW(new_for_object, __VA_ARGS__) # define dex_future_new_take_object(...) _DEX_FUTURE_NEW(new_take_object, __VA_ARGS__) # define dex_future_new_for_pointer(...) _DEX_FUTURE_NEW(new_for_pointer, __VA_ARGS__) #endif #define dex_future_new_true() dex_future_new_for_boolean(TRUE) #define dex_future_new_false() dex_future_new_for_boolean(FALSE) G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexFuture, dex_unref) G_END_DECLS 07070100000042000081A40000000000000000000000016712D33C00009743000000000000000000000000000000000000001B00000000libdex-0.8.1/src/dex-gio.c/* * dex-gio.c * * Copyright 2022-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-async-pair-private.h" #include "dex-future-private.h" #include "dex-future-set.h" #include "dex-gio.h" #include "dex-promise.h" typedef struct _DexFileInfoList DexFileInfoList; static DexFileInfoList * dex_file_info_list_copy (DexFileInfoList *list) { return (DexFileInfoList *)g_list_copy_deep ((GList *)list, (GCopyFunc)g_object_ref, NULL); } static void dex_file_info_list_free (DexFileInfoList *list) { GList *real_list = (GList *)list; g_list_free_full (real_list, g_object_unref); } typedef struct _DexInetAddressList DexInetAddressList; G_DEFINE_BOXED_TYPE (DexFileInfoList, dex_file_info_list, dex_file_info_list_copy, dex_file_info_list_free) static DexInetAddressList * dex_inet_address_list_copy (DexInetAddressList *list) { return (DexInetAddressList *)g_list_copy_deep ((GList *)list, (GCopyFunc)g_object_ref, NULL); } static void dex_inet_address_list_free (DexInetAddressList *list) { GList *real_list = (GList *)list; g_list_free_full (real_list, g_object_unref); } G_DEFINE_BOXED_TYPE (DexInetAddressList, dex_inet_address_list, dex_inet_address_list_copy, dex_inet_address_list_free) static inline DexAsyncPair * create_async_pair (const char *name) { DexAsyncPair *async_pair; async_pair = (DexAsyncPair *)dex_object_create_instance (DEX_TYPE_ASYNC_PAIR); dex_future_set_static_name (DEX_FUTURE (async_pair), name); return async_pair; } static void dex_input_stream_read_bytes_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; GBytes *bytes; bytes = g_input_stream_read_bytes_finish (G_INPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_boxed (async_pair, G_TYPE_BYTES, bytes); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_input_stream_read_bytes: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_input_stream_read_bytes (GInputStream *stream, gsize count, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL); async_pair = create_async_pair (G_STRFUNC); g_input_stream_read_bytes_async (stream, count, priority, async_pair->cancellable, dex_input_stream_read_bytes_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_output_stream_write_bytes_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; gssize len; len = g_output_stream_write_bytes_finish (G_OUTPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_int64 (async_pair, len); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_output_stream_write_bytes: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_output_stream_write_bytes (GOutputStream *stream, GBytes *bytes, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), NULL); async_pair = create_async_pair (G_STRFUNC); g_output_stream_write_bytes_async (stream, bytes, priority, async_pair->cancellable, dex_output_stream_write_bytes_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_read_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GFileInputStream *stream; GError *error = NULL; if ((stream = g_file_read_finish (G_FILE (object), result, &error))) dex_async_pair_return_object (async_pair, stream); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_read: * @file: a #GFile * @io_priority: IO priority such as %G_PRIORITY_DEFAULT * * Asynchronously opens a file for reading. * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_read (GFile *file, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE (file), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_read_async (file, io_priority, async_pair->cancellable, dex_file_read_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_replace_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GFileOutputStream *stream; GError *error = NULL; if ((stream = g_file_replace_finish (G_FILE (object), result, &error))) dex_async_pair_return_object (async_pair, stream); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_replace: * @etag: (nullable) * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_replace (GFile *file, const char *etag, gboolean make_backup, GFileCreateFlags flags, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE (file), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_replace_async (file, etag, make_backup, flags, priority, async_pair->cancellable, dex_file_replace_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_input_stream_read_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; gssize len; len = g_input_stream_read_finish (G_INPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_int64 (async_pair, len); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_input_stream_read: * @buffer: (array length=count) (element-type guint8) (out caller-allocates) * @count: (in) * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_input_stream_read (GInputStream *self, gpointer buffer, gsize count, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_INPUT_STREAM (self), NULL); async_pair = create_async_pair (G_STRFUNC); g_input_stream_read_async (self, buffer, count, priority, async_pair->cancellable, dex_input_stream_read_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_input_stream_skip_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; gssize len; len = g_input_stream_skip_finish (G_INPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_int64 (async_pair, len); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_input_stream_skip: * @count: the number of bytes to skip * @io_priority: %G_PRIORITY_DEFAULT or similar priority value * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_input_stream_skip (GInputStream *self, gsize count, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_INPUT_STREAM (self), NULL); async_pair = create_async_pair (G_STRFUNC); g_input_stream_skip_async (self, count, io_priority, async_pair->cancellable, dex_input_stream_skip_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_output_stream_write_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; gssize len; len = g_output_stream_write_finish (G_OUTPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_int64 (async_pair, len); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_output_stream_write: * @buffer: (array length=count) (element-type guint8) * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_output_stream_write (GOutputStream *self, gconstpointer buffer, gsize count, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_OUTPUT_STREAM (self), NULL); async_pair = create_async_pair (G_STRFUNC); g_output_stream_write_async (self, buffer, count, priority, async_pair->cancellable, dex_output_stream_write_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_output_stream_close_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; g_output_stream_close_finish (G_OUTPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_boolean (async_pair, TRUE); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_output_stream_close: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_output_stream_close (GOutputStream *self, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_OUTPUT_STREAM (self), NULL); async_pair = create_async_pair (G_STRFUNC); g_output_stream_close_async (self, priority, async_pair->cancellable, dex_output_stream_close_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_input_stream_close_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; g_input_stream_close_finish (G_INPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_boolean (async_pair, TRUE); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_input_stream_close: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_input_stream_close (GInputStream *self, int priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_INPUT_STREAM (self), NULL); async_pair = create_async_pair (G_STRFUNC); g_input_stream_close_async (self, priority, async_pair->cancellable, dex_input_stream_close_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_output_stream_splice_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; gssize len; len = g_output_stream_splice_finish (G_OUTPUT_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_int64 (async_pair, len); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_output_stream_splice: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_output_stream_splice (GOutputStream *output, GInputStream *input, GOutputStreamSpliceFlags flags, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), NULL); g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); async_pair = create_async_pair (G_STRFUNC); g_output_stream_splice_async (output, input, flags, io_priority, async_pair->cancellable, dex_output_stream_splice_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_query_info_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; GFileInfo *info; info = g_file_query_info_finish (G_FILE (object), result, &error); if (error == NULL) dex_async_pair_return_object (async_pair, info); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_query_info: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_query_info (GFile *file, const char *attributes, GFileQueryInfoFlags flags, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE (file), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_query_info_async (file, attributes, flags, io_priority, async_pair->cancellable, dex_file_query_info_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_make_directory_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; g_file_make_directory_finish (G_FILE (object), result, &error); if (error == NULL) dex_async_pair_return_boolean (async_pair, TRUE); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_make_directory: * @file: a #GFile * @io_priority: IO priority such as %G_PRIORITY_DEFAULT * * Asynchronously creates a directory and returns #DexFuture which * can be observed for the result. * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_make_directory (GFile *file, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE (file), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_make_directory_async (file, io_priority, async_pair->cancellable, dex_file_make_directory_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_enumerate_children_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; GFileEnumerator *enumerator; enumerator = g_file_enumerate_children_finish (G_FILE (object), result, &error); if (error == NULL) dex_async_pair_return_object (async_pair, enumerator); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_enumerate_children: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_enumerate_children (GFile *file, const char *attributes, GFileQueryInfoFlags flags, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE (file), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_enumerate_children_async (file, attributes, flags, io_priority, async_pair->cancellable, dex_file_enumerate_children_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_enumerator_next_files_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; GList *infos; infos = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (object), result, &error); if (error == NULL) dex_async_pair_return_boxed (async_pair, DEX_TYPE_FILE_INFO_LIST, infos); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_enumerator_next_files: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_enumerator_next_files (GFileEnumerator *file_enumerator, int num_files, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE_ENUMERATOR (file_enumerator), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_enumerator_next_files_async (file_enumerator, num_files, io_priority, async_pair->cancellable, dex_file_enumerator_next_files_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_copy_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; g_file_copy_finish (G_FILE (object), result, &error); if (error == NULL) dex_async_pair_return_boolean (async_pair, TRUE); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_copy: * @source: a #GFile * @destination: a #GFile * @flags: the #GFileCopyFlags * @io_priority: IO priority such as %G_PRIORITY_DEFAULT * * Asynchronously copies a file and returns a #DexFuture which * can be observed for the result. * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_copy (GFile *source, GFile *destination, GFileCopyFlags flags, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE (source), NULL); g_return_val_if_fail (G_IS_FILE (destination), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_copy_async (source, destination, flags, io_priority, async_pair->cancellable, NULL, NULL, /* TODO: Progress support */ dex_file_copy_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_socket_listener_accept_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GSocketConnection *conn; GError *error = NULL; conn = g_socket_listener_accept_finish (G_SOCKET_LISTENER (object), result, NULL, &error); if (error == NULL) dex_async_pair_return_object (async_pair, conn); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_socket_listener_accept: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_socket_listener_accept (GSocketListener *listener) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_SOCKET_LISTENER (listener), NULL); async_pair = create_async_pair (G_STRFUNC); g_socket_listener_accept_async (listener, async_pair->cancellable, dex_socket_listener_accept_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_socket_client_connect_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GSocketConnection *conn; GError *error = NULL; conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (object), result, &error); if (error == NULL) dex_async_pair_return_object (async_pair, conn); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_socket_client_connect: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_socket_client_connect (GSocketClient *socket_client, GSocketConnectable *socket_connectable) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_SOCKET_CLIENT (socket_client), NULL); g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (socket_connectable), NULL); async_pair = create_async_pair (G_STRFUNC); g_socket_client_connect_async (socket_client, socket_connectable, async_pair->cancellable, dex_socket_client_connect_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_io_stream_close_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; g_io_stream_close_finish (G_IO_STREAM (object), result, &error); if (error == NULL) dex_async_pair_return_boolean (async_pair, TRUE); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_io_stream_close: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_io_stream_close (GIOStream *io_stream, int io_priority) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_IO_STREAM (io_stream), NULL); async_pair = create_async_pair (G_STRFUNC); g_io_stream_close_async (io_stream, io_priority, async_pair->cancellable, dex_io_stream_close_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_resolver_lookup_by_name_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; GList *list; list = g_resolver_lookup_by_name_finish (G_RESOLVER (object), result, &error); if (error == NULL) dex_async_pair_return_boxed (async_pair, DEX_TYPE_INET_ADDRESS_LIST, list); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_resolver_lookup_by_name: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_resolver_lookup_by_name (GResolver *resolver, const char *address) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL); g_return_val_if_fail (address != NULL, NULL); async_pair = create_async_pair (G_STRFUNC); g_resolver_lookup_by_name_async (resolver, address, async_pair->cancellable, dex_resolver_lookup_by_name_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_load_contents_bytes_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; char *contents = NULL; gsize len = 0; g_file_load_contents_finish (G_FILE (object), result, &contents, &len, NULL, &error); if (error == NULL) dex_async_pair_return_boxed (async_pair, G_TYPE_BYTES, g_bytes_new_take (contents, len)); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_file_load_contents_bytes: * * Returns: (transfer full): a #DexFuture */ DexFuture * dex_file_load_contents_bytes (GFile *file) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_FILE (file), NULL); async_pair = create_async_pair (G_STRFUNC); g_file_load_contents_async (file, async_pair->cancellable, dex_file_load_contents_bytes_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_dbus_connection_send_message_with_reply_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GDBusMessage *message = NULL; GError *error = NULL; message = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (object), result, &error); if (error == NULL) dex_async_pair_return_object (async_pair, message); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_dbus_connection_send_message_with_reply: * @connection: a #GDBusConnection * @message: a #GDBusMessage * @flags: flags for @message * @timeout_msec: timeout in milliseconds, or -1 for default, or %G_MAXINT * for no timeout. * @out_serial: (out) (optional): a location for the message serial number * * Wrapper for g_dbus_connection_send_message_with_reply(). * * Returns: (transfer full): a #DexFuture that will resolve to a #GDBusMessage * or reject with failure. * * Since: 0.4 */ DexFuture * dex_dbus_connection_send_message_with_reply (GDBusConnection *connection, GDBusMessage *message, GDBusSendMessageFlags flags, int timeout_msec, guint32 *out_serial) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); async_pair = create_async_pair (G_STRFUNC); g_dbus_connection_send_message_with_reply (connection, message, flags, timeout_msec, out_serial, async_pair->cancellable, dex_dbus_connection_send_message_with_reply_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_dbus_connection_call_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GVariant *reply = NULL; GError *error = NULL; reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error); if (error == NULL) dex_async_pair_return_variant (async_pair, reply); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_dbus_connection_call: * @bus_name: (nullable) * @object_path: * @interface_name: * @method_name: * @parameters: (nullable) * @reply_type: (nullable) * @flags: * @timeout_msec: * * Wrapper for g_dbus_connection_call(). * * Returns: (transfer full): a #DexFuture that resolves to a #GVariant * or rejects with error. * * Since: 0.4 */ DexFuture * dex_dbus_connection_call (GDBusConnection *connection, const char *bus_name, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, const GVariantType *reply_type, GDBusCallFlags flags, int timeout_msec) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); async_pair = create_async_pair (G_STRFUNC); g_dbus_connection_call (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, async_pair->cancellable, dex_dbus_connection_call_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } #ifdef G_OS_UNIX static void dex_dbus_connection_call_with_unix_fd_list_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexFutureSet *future_set = user_data; DexAsyncPair *async_pair; DexPromise *promise; GUnixFDList *fd_list = NULL; GVariant *reply = NULL; GError *error = NULL; g_assert (G_IS_DBUS_CONNECTION (object)); g_assert (DEX_IS_FUTURE_SET (future_set)); async_pair = DEX_ASYNC_PAIR (dex_future_set_get_future_at (future_set, 0)); promise = DEX_PROMISE (dex_future_set_get_future_at (future_set, 1)); g_assert (DEX_IS_ASYNC_PAIR (async_pair)); g_assert (DEX_IS_PROMISE (promise)); reply = g_dbus_connection_call_with_unix_fd_list_finish (G_DBUS_CONNECTION (object), &fd_list, result, &error); g_assert (!fd_list || G_IS_UNIX_FD_LIST (fd_list)); g_assert (reply != NULL || error != NULL); if (error == NULL) { dex_promise_resolve_object (promise, fd_list); dex_async_pair_return_variant (async_pair, reply); } else { dex_promise_reject (promise, g_error_copy (error)); dex_async_pair_return_error (async_pair, error); } dex_unref (future_set); } /** * dex_dbus_connection_call_with_unix_fd_list: * @bus_name: (nullable) * @object_path: * @interface_name: * @method_name: * @parameters: (nullable) * @reply_type: (nullable) * @flags: * @timeout_msec: * @fd_list: (nullable): a #GUnixFDList * * Wrapper for g_dbus_connection_call_with_unix_fd_list(). * * Returns: (transfer full): a #DexFutureSet that resolves to a #GVariant. * The #DexFuture containing the resulting #GUnixFDList can be retrieved * with dex_future_set_get_future_at() with an index of 1. * * Since: 0.4 */ DexFuture * dex_dbus_connection_call_with_unix_fd_list (GDBusConnection *connection, const char *bus_name, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, const GVariantType *reply_type, GDBusCallFlags flags, int timeout_msec, GUnixFDList *fd_list) { DexAsyncPair *async_pair; DexPromise *promise; DexFuture *ret; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); g_return_val_if_fail (!fd_list || G_IS_UNIX_FD_LIST (fd_list), NULL); /* Will hold our GVariant result */ async_pair = create_async_pair (G_STRFUNC); /* Will hold our GUnixFDList result */ promise = dex_promise_new (); /* Sent to user. Resolving will contain variant. */ ret = dex_future_all (DEX_FUTURE (async_pair), DEX_FUTURE (promise), NULL); g_dbus_connection_call_with_unix_fd_list (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, fd_list, async_pair->cancellable, dex_dbus_connection_call_with_unix_fd_list_cb, dex_ref (ret)); return ret; } #endif static void dex_bus_get_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GDBusConnection *bus; GError *error = NULL; bus = g_bus_get_finish (result, &error); if (error == NULL) dex_async_pair_return_object (async_pair, bus); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_bus_get: * @bus_type: * * Wrapper for g_bus_get(). * * Returns: (transfer full): a #DexFuture that resolves to a #GDBusConnection * or rejects with error. * * Since: 0.4 */ DexFuture * dex_bus_get (GBusType bus_type) { DexAsyncPair *async_pair; async_pair = create_async_pair (G_STRFUNC); g_bus_get (bus_type, async_pair->cancellable, dex_bus_get_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_subprocess_wait_check_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexAsyncPair *async_pair = user_data; GError *error = NULL; if (g_subprocess_wait_check_finish (G_SUBPROCESS (object), result, &error)) dex_async_pair_return_boolean (async_pair, TRUE); else dex_async_pair_return_error (async_pair, error); dex_unref (async_pair); } /** * dex_subprocess_wait_check: * @subprocess: a #GSubprocess * * Creates a future that awaits for @subprocess to complete using * g_subprocess_wait_check_async(). * * Returns: (transfer full): a #DexFuture that will resolve when @subprocess * exits cleanly or reject upon signal or non-successful exit. * * Since: 0.4 */ DexFuture * dex_subprocess_wait_check (GSubprocess *subprocess) { DexAsyncPair *async_pair; g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), NULL); async_pair = create_async_pair (G_STRFUNC); g_subprocess_wait_check_async (subprocess, async_pair->cancellable, dex_subprocess_wait_check_cb, dex_ref (async_pair)); return DEX_FUTURE (async_pair); } static void dex_file_query_exists_cb (GObject *object, GAsyncResult *result, gpointer user_data) { DexPromise *promise = user_data; GFileInfo *file_info; GError *error = NULL; file_info = g_file_query_info_finish (G_FILE (object), result, &error); if (file_info != NULL) dex_promise_resolve_boolean (promise, TRUE); else dex_promise_reject (promise, g_steal_pointer (&error)); g_clear_object (&file_info); dex_unref (promise); } /** * dex_file_query_exists: * @file: a #GFile * * Queries to see if @file exists asynchronously. * * Returns: (transfer full): a #DexFuture that will resolve with %TRUE * if the file exists, otherwise reject with error. * * Since: 0.6 */ DexFuture * dex_file_query_exists (GFile *file) { DexPromise *promise; g_return_val_if_fail (G_IS_FILE (file), NULL); promise = dex_promise_new_cancellable (); g_file_query_info_async (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, dex_promise_get_cancellable (promise), dex_file_query_exists_cb, dex_ref (promise)); return DEX_FUTURE (promise); } 07070100000043000081A40000000000000000000000016712D33C00002867000000000000000000000000000000000000001B00000000libdex-0.8.1/src/dex-gio.h/* * dex-gio.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gio/gio.h> #ifdef G_OS_UNIX # include <gio/gunixfdlist.h> #endif #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_FILE_INFO_LIST (dex_file_info_list_get_type()) #define DEX_TYPE_INET_ADDRESS_LIST (dex_inet_address_list_get_type()) DEX_AVAILABLE_IN_ALL GType dex_file_info_list_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL GType dex_inet_address_list_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_make_directory (GFile *file, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_copy (GFile *source, GFile *destination, GFileCopyFlags flags, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_read (GFile *file, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_load_contents_bytes (GFile *file) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_query_info (GFile *file, const char *attributes, GFileQueryInfoFlags flags, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_replace (GFile *file, const char *etag, gboolean make_backup, GFileCreateFlags flags, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_enumerate_children (GFile *file, const char *attributes, GFileQueryInfoFlags flags, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_enumerator_next_files (GFileEnumerator *file_enumerator, int num_files, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_input_stream_close (GInputStream *self, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_input_stream_read (GInputStream *self, gpointer buffer, gsize count, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_input_stream_skip (GInputStream *self, gsize count, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_input_stream_read_bytes (GInputStream *self, gsize count, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_output_stream_close (GOutputStream *self, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_output_stream_splice (GOutputStream *output, GInputStream *input, GOutputStreamSpliceFlags flags, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_output_stream_write (GOutputStream *self, gconstpointer buffer, gsize count, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_output_stream_write_bytes (GOutputStream *self, GBytes *bytes, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_socket_listener_accept (GSocketListener *listener) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_socket_client_connect (GSocketClient *socket_client, GSocketConnectable *socket_connectable) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_io_stream_close (GIOStream *io_stream, int io_priority) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_resolver_lookup_by_name (GResolver *resolver, const char *address) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_bus_get (GBusType bus_type) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_dbus_connection_call (GDBusConnection *connection, const char *bus_name, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, const GVariantType *reply_type, GDBusCallFlags flags, int timeout_msec) G_GNUC_WARN_UNUSED_RESULT; #ifdef G_OS_UNIX DEX_AVAILABLE_IN_ALL DexFuture *dex_dbus_connection_call_with_unix_fd_list (GDBusConnection *connection, const char *bus_name, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, const GVariantType *reply_type, GDBusCallFlags flags, int timeout_msec, GUnixFDList *fd_list) G_GNUC_WARN_UNUSED_RESULT; #endif DEX_AVAILABLE_IN_ALL DexFuture *dex_dbus_connection_send_message_with_reply (GDBusConnection *connection, GDBusMessage *message, GDBusSendMessageFlags flags, int timeout_msec, guint32 *out_serial) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_subprocess_wait_check (GSubprocess *subprocess) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexFuture *dex_file_query_exists (GFile *file) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS 07070100000044000081A40000000000000000000000016712D33C000004C1000000000000000000000000000000000000002800000000libdex-0.8.1/src/dex-infinite-private.h/* * dex-infinite-private.h * * Copyright 2023 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future-private.h" G_BEGIN_DECLS #define DEX_TYPE_INFINITE (dex_infinite_get_type()) #define DEX_INFINITE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_INFINITE, DexInfinite)) #define DEX_IS_INFINITE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_INFINITE)) GType dex_infinite_get_type (void) G_GNUC_CONST; DexFuture *dex_infinite_new (void); G_END_DECLS 07070100000045000081A40000000000000000000000016712D33C000006ED000000000000000000000000000000000000002000000000libdex-0.8.1/src/dex-infinite.c/* * dex-infinite.c * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-infinite-private.h" typedef struct _DexInfinite { DexFuture parent_instance; } DexInfinite; typedef struct _DexInfiniteClass { DexFutureClass parent_class; } DexInfiniteClass; DEX_DEFINE_FINAL_TYPE (DexInfinite, dex_infinite, DEX_TYPE_FUTURE) #undef DEX_TYPE_INFINITE #define DEX_TYPE_INFINITE dex_infinite_type static void dex_infinite_discard (DexFuture *future) { } static gboolean dex_infinite_propagate (DexFuture *future, DexFuture *completed) { g_return_val_if_reached (FALSE); } static void dex_infinite_class_init (DexInfiniteClass *infinite_class) { DexFutureClass *future_class = DEX_FUTURE_CLASS (infinite_class); future_class->propagate = dex_infinite_propagate; future_class->discard = dex_infinite_discard; } static void dex_infinite_init (DexInfinite *infinite) { } DexFuture * dex_infinite_new (void) { return (DexFuture *)dex_object_create_instance (dex_infinite_type); } 07070100000046000081A40000000000000000000000016712D33C00000A59000000000000000000000000000000000000001C00000000libdex-0.8.1/src/dex-init.c/* libdex.c * * Copyright 2022 Christian Hergert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "libdex.h" #include "dex-main-scheduler-private.h" #include "dex-infinite-private.h" #include "dex-scheduler-private.h" #include "dex-semaphore-private.h" #include "dex-thread-pool-worker-private.h" #ifdef HAVE_LIBURING # include "dex-uring-future-private.h" #endif #include "gconstructor.h" static void dex_init_once (void) { DexMainScheduler *main_scheduler; (void)dex_error_quark (); /* Base object, always register first */ g_type_ensure (DEX_TYPE_OBJECT); /* Scheduler type */ g_type_ensure (DEX_TYPE_SCHEDULER); g_type_ensure (DEX_TYPE_MAIN_SCHEDULER); g_type_ensure (DEX_TYPE_THREAD_POOL_SCHEDULER); g_type_ensure (DEX_TYPE_THREAD_POOL_WORKER); /* Future types */ g_type_ensure (DEX_TYPE_FUTURE); g_type_ensure (DEX_TYPE_ASYNC_PAIR); g_type_ensure (DEX_TYPE_FIBER); g_type_ensure (DEX_TYPE_FUTURE_SET); g_type_ensure (DEX_TYPE_BLOCK); g_type_ensure (DEX_TYPE_CANCELLABLE); g_type_ensure (DEX_TYPE_PROMISE); g_type_ensure (DEX_TYPE_STATIC_FUTURE); g_type_ensure (DEX_TYPE_TIMEOUT); g_type_ensure (DEX_TYPE_INFINITE); #ifdef G_OS_UNIX g_type_ensure (DEX_TYPE_UNIX_SIGNAL); #endif #ifdef HAVE_LIBURING g_type_ensure (DEX_TYPE_URING_FUTURE); #endif /* Misc types */ g_type_ensure (DEX_TYPE_ASYNC_RESULT); g_type_ensure (DEX_TYPE_CHANNEL); g_type_ensure (DEX_TYPE_SEMAPHORE); /* Setup default scheduler for application */ main_scheduler = dex_main_scheduler_new (NULL); dex_scheduler_set_default (DEX_SCHEDULER (main_scheduler)); } void dex_init (void) { static gsize initialized; if (g_once_init_enter (&initialized)) { dex_init_once (); g_once_init_leave (&initialized, TRUE); } } G_DEFINE_CONSTRUCTOR (dex_init_ctor) static void dex_init_ctor (void) { dex_init (); } 07070100000047000081A40000000000000000000000016712D33C000003CC000000000000000000000000000000000000001C00000000libdex-0.8.1/src/dex-init.h/* * dex-init.h * * Copyright 2022 Christian Hergert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-version-macros.h" G_BEGIN_DECLS DEX_AVAILABLE_IN_ALL void dex_init (void); G_END_DECLS 07070100000048000081A40000000000000000000000016712D33C000003C7000000000000000000000000000000000000002E00000000libdex-0.8.1/src/dex-main-scheduler-private.h/* * dex-main-scheduler-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-main-scheduler.h" G_BEGIN_DECLS DexMainScheduler *dex_main_scheduler_new (GMainContext *main_context); G_END_DECLS 07070100000049000081A40000000000000000000000016712D33C00001F64000000000000000000000000000000000000002600000000libdex-0.8.1/src/dex-main-scheduler.c/* * dex-main-scheduler.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-aio-backend-private.h" #include "dex-fiber-private.h" #include "dex-main-scheduler-private.h" #include "dex-scheduler-private.h" #include "dex-work-queue-private.h" #include "dex-thread-storage-private.h" /** * DexMainScheduler: * * #DexMainScheduler is the scheduler used on the default thread of an * application. It is meant to integrate with your main loop. * * This scheduler does the bulk of the work in an application. * * Use #DexThreadPoolScheduler when you want to offload work to a thread * and still use future-based programming. */ typedef struct _DexMainWorkQueueItem { DexWorkItem work_item; GList link; } DexMainWorkQueueItem; typedef struct _DexMainWorkQueueSource { GSource source; DexObject *object; GQueue *queue; } DexMainWorkQueueSource; typedef struct _DexMainScheduler { DexScheduler parent_scheduler; GMainContext *main_context; GSource *aio_context; GSource *fiber_scheduler; GSource *work_queue_source; GQueue work_queue; } DexMainScheduler; typedef struct _DexMainSchedulerClass { DexSchedulerClass parent_class; } DexMainSchedulerClass; DEX_DEFINE_FINAL_TYPE (DexMainScheduler, dex_main_scheduler, DEX_TYPE_SCHEDULER) #undef DEX_TYPE_MAIN_SCHEDULER #define DEX_TYPE_MAIN_SCHEDULER dex_main_scheduler_type static gboolean dex_main_work_queue_check (GSource *source) { DexMainWorkQueueSource *wqs = (DexMainWorkQueueSource *)source; gboolean ret; dex_object_lock (wqs->object); ret = wqs->queue->length > 0; dex_object_unlock (wqs->object); return ret; } static gboolean dex_main_work_queue_prepare (GSource *source, int *timeout) { *timeout = -1; return dex_main_work_queue_check (source); } static gboolean dex_main_work_queue_dispatch (GSource *source, GSourceFunc callback, gpointer callback_data) { DexMainWorkQueueSource *wqs = (DexMainWorkQueueSource *)source; GQueue queue; dex_object_lock (wqs->object); queue = *wqs->queue; *wqs->queue = (GQueue) {NULL, NULL, 0}; dex_object_unlock (wqs->object); while (queue.length > 0) { DexMainWorkQueueItem *item = g_queue_pop_head_link (&queue)->data; dex_work_item_invoke (&item->work_item); g_free (item); } return G_SOURCE_CONTINUE; } static GSourceFuncs dex_main_work_queue_source_funcs = { .check = dex_main_work_queue_check, .dispatch = dex_main_work_queue_dispatch, .prepare = dex_main_work_queue_prepare, }; static void dex_main_scheduler_push (DexScheduler *scheduler, DexWorkItem work_item) { DexMainScheduler *main_scheduler = DEX_MAIN_SCHEDULER (scheduler); DexMainWorkQueueItem *item; g_assert (DEX_IS_MAIN_SCHEDULER (main_scheduler)); item = g_new0 (DexMainWorkQueueItem, 1); item->work_item = work_item; item->link.data = item; dex_object_lock (main_scheduler); g_queue_push_tail_link (&main_scheduler->work_queue, &item->link); dex_object_unlock (main_scheduler); if G_UNLIKELY (scheduler != dex_thread_storage_get ()->scheduler) g_main_context_wakeup (main_scheduler->main_context); } static GMainContext * dex_main_scheduler_get_main_context (DexScheduler *scheduler) { DexMainScheduler *main_scheduler = DEX_MAIN_SCHEDULER (scheduler); g_assert (DEX_IS_MAIN_SCHEDULER (main_scheduler)); return main_scheduler->main_context; } static DexAioContext * dex_main_scheduler_get_aio_context (DexScheduler *scheduler) { DexMainScheduler *main_scheduler = DEX_MAIN_SCHEDULER (scheduler); g_assert (DEX_IS_MAIN_SCHEDULER (main_scheduler)); return (DexAioContext *)main_scheduler->aio_context; } static void dex_main_scheduler_spawn (DexScheduler *scheduler, DexFiber *fiber) { DexMainScheduler *main_scheduler = DEX_MAIN_SCHEDULER (scheduler); g_assert (DEX_IS_MAIN_SCHEDULER (main_scheduler)); dex_fiber_scheduler_register ((DexFiberScheduler *)main_scheduler->fiber_scheduler, fiber); } static void dex_main_scheduler_finalize (DexObject *object) { DexMainScheduler *main_scheduler = DEX_MAIN_SCHEDULER (object); /* Flush out any pending work items */ while (main_scheduler->work_queue.length) { DexMainWorkQueueItem *item = g_queue_pop_head_link (&main_scheduler->work_queue)->data; dex_work_item_invoke (&item->work_item); g_free (item); } /* Clear DexAioBackend context */ g_source_destroy (main_scheduler->aio_context); g_clear_pointer (&main_scheduler->aio_context, g_source_unref); /* Clear DexFiberScheduler context */ g_source_destroy (main_scheduler->fiber_scheduler); g_clear_pointer (&main_scheduler->fiber_scheduler, g_source_unref); /* Clear work queue source */ g_source_destroy (main_scheduler->work_queue_source); g_clear_pointer (&main_scheduler->work_queue_source, g_source_unref); /* Release our main context */ g_clear_pointer (&main_scheduler->main_context, g_main_context_unref); DEX_OBJECT_CLASS (dex_main_scheduler_parent_class)->finalize (object); } static void dex_main_scheduler_class_init (DexMainSchedulerClass *main_scheduler_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (main_scheduler_class); DexSchedulerClass *scheduler_class = DEX_SCHEDULER_CLASS (main_scheduler_class); object_class->finalize = dex_main_scheduler_finalize; scheduler_class->get_aio_context = dex_main_scheduler_get_aio_context; scheduler_class->get_main_context = dex_main_scheduler_get_main_context; scheduler_class->push = dex_main_scheduler_push; scheduler_class->spawn = dex_main_scheduler_spawn; } static void dex_main_scheduler_init (DexMainScheduler *main_scheduler) { } DexMainScheduler * dex_main_scheduler_new (GMainContext *main_context) { DexMainWorkQueueSource *work_queue_source; DexFiberScheduler *fiber_scheduler; DexMainScheduler *main_scheduler; DexAioBackend *aio_backend; DexAioContext *aio_context; if (main_context == NULL) main_context = g_main_context_default (); aio_backend = dex_aio_backend_get_default (); aio_context = dex_aio_backend_create_context (aio_backend); fiber_scheduler = dex_fiber_scheduler_new (); main_scheduler = (DexMainScheduler *)dex_object_create_instance (DEX_TYPE_MAIN_SCHEDULER); main_scheduler->main_context = g_main_context_ref (main_context); main_scheduler->aio_context = (GSource *)aio_context; main_scheduler->fiber_scheduler = (GSource *)fiber_scheduler; work_queue_source = (DexMainWorkQueueSource *) g_source_new (&dex_main_work_queue_source_funcs, sizeof *work_queue_source); work_queue_source->object = DEX_OBJECT (main_scheduler); work_queue_source->queue = &main_scheduler->work_queue; main_scheduler->work_queue_source = (GSource *)work_queue_source; dex_thread_storage_get ()->aio_context = aio_context; dex_thread_storage_get ()->scheduler = DEX_SCHEDULER (main_scheduler); g_source_attach (main_scheduler->aio_context, main_context); g_source_attach (main_scheduler->fiber_scheduler, main_context); g_source_attach (main_scheduler->work_queue_source, main_context); return main_scheduler; } 0707010000004A000081A40000000000000000000000016712D33C00000542000000000000000000000000000000000000002600000000libdex-0.8.1/src/dex-main-scheduler.h/* * dex-main-scheduler.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-scheduler.h" G_BEGIN_DECLS #define DEX_TYPE_MAIN_SCHEDULER (dex_main_scheduler_get_type()) #define DEX_MAIN_SCHEDULER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_MAIN_SCHEDULER, DexMainScheduler)) #define DEX_IS_MAIN_SCHEDULER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_MAIN_SCHEDULER)) typedef struct _DexMainScheduler DexMainScheduler; DEX_AVAILABLE_IN_ALL GType dex_main_scheduler_get_type (void) G_GNUC_CONST; G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexMainScheduler, dex_unref) G_END_DECLS 0707010000004B000081A40000000000000000000000016712D33C000016A4000000000000000000000000000000000000002600000000libdex-0.8.1/src/dex-object-private.h/* * dex-object-private.h * * Copyright 2022-2023 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-compat-private.h" #include "dex-object.h" G_BEGIN_DECLS #define DEX_OBJECT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, DEX_TYPE_OBJECT, DexObjectClass) #define DEX_OBJECT_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, DEX_TYPE_OBJECT, DexObjectClass) #define _DEX_DEFINE_TYPE(ClassName, class_name, PARENT_TYPE, Flags) \ static void G_PASTE(class_name, _class_init) (G_PASTE (ClassName, Class) *); \ static void G_PASTE(class_name, _init) (ClassName *); \ static gpointer G_PASTE(class_name, _parent_class); \ static GType G_PASTE(class_name, _type); \ \ static void \ G_PASTE(class_name, _class_intern_init) (gpointer klass) \ { \ G_PASTE (class_name, _parent_class) = g_type_class_peek_parent (klass); \ G_PASTE (class_name, _class_init) ((G_PASTE (ClassName, Class) *)klass); \ } \ \ GType G_PASTE(class_name, _get_type) (void) \ { \ if (g_once_init_enter (&G_PASTE (class_name, _type))) \ { \ GType gtype = g_type_register_static_simple ( \ PARENT_TYPE, \ g_intern_static_string (G_STRINGIFY (ClassName)), \ sizeof (G_PASTE (ClassName, Class)), \ (GClassInitFunc) G_PASTE(class_name, _class_intern_init), \ sizeof (ClassName), \ (GInstanceInitFunc) G_PASTE(class_name, _init), \ Flags); \ g_once_init_leave (&G_PASTE (class_name, _type), gtype); \ } \ return G_PASTE(class_name, _type); \ } #define DEX_DEFINE_ABSTRACT_TYPE(ClassName, class_name, PARENT_TYPE) \ _DEX_DEFINE_TYPE(ClassName, class_name, PARENT_TYPE, G_TYPE_FLAG_ABSTRACT) #define DEX_DEFINE_DERIVABLE_TYPE(ClassName, class_name, PARENT_TYPE) \ _DEX_DEFINE_TYPE(ClassName, class_name, PARENT_TYPE, 0) #define DEX_DEFINE_FINAL_TYPE(ClassName, class_name, PARENT_TYPE) \ _DEX_DEFINE_TYPE(ClassName, class_name, PARENT_TYPE, G_TYPE_FLAG_FINAL) #if defined(_MSC_VER) # define DEX_ALIGNED_BEGIN(_N) __declspec(align (_N)) # define DEX_ALIGNED_END(_N) #else # define DEX_ALIGNED_BEGIN(_N) # define DEX_ALIGNED_END(_N) __attribute__ ((aligned (_N))) #endif typedef struct _DexWeakRef { GMutex mutex; struct _DexWeakRef *next; struct _DexWeakRef *prev; gpointer mem_block; } DexWeakRef; void dex_weak_ref_clear (DexWeakRef *weak_ref); void dex_weak_ref_init (DexWeakRef *weak_ref, gpointer mem_block); gpointer dex_weak_ref_get (DexWeakRef *weak_ref); void dex_weak_ref_set (DexWeakRef *weak_ref, gpointer mem_block); DEX_ALIGNED_BEGIN (8) typedef struct _DexObject { GTypeInstance parent_instance; GMutex mutex; DexWeakRef *weak_refs; guint weak_refs_watermark; _Atomic int ref_count; #ifdef HAVE_SYSPROF gint64 ctime; #endif } DexObject DEX_ALIGNED_END (8); static inline void dex_object_lock (gpointer data) { g_mutex_lock (&DEX_OBJECT (data)->mutex); } static inline void dex_object_unlock (gpointer data) { g_mutex_unlock (&DEX_OBJECT (data)->mutex); } typedef struct _DexObjectClass { GTypeClass parent_class; void (*finalize) (DexObject *object); } DexObjectClass; DexObject *dex_object_create_instance (GType instance_type); G_END_DECLS 0707010000004C000081A40000000000000000000000016712D33C0000465E000000000000000000000000000000000000001E00000000libdex-0.8.1/src/dex-object.c/* * dex-object.c * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <stdatomic.h> #include <gobject/gvaluecollector.h> #include "dex-object-private.h" #include "dex-profiler.h" /** * DexObject: (ref-func dex_ref) (unref-func dex_unref) * (set-value-func dex_value_set_object) * (get-value-func dex_value_get_object) * * `DexObject` is the basic building block of types defined within * libdex. Futures, Schedulers, and Channels all inherit from DexObject * which provides features like thread-safe weak pointers and memory * management operations. * * Objects that are integrating with GIO instead inherit from their * natural type in GIO. */ static GType dex_object_type = G_TYPE_INVALID; #undef DEX_TYPE_OBJECT #define DEX_TYPE_OBJECT dex_object_type static void dex_object_finalize (DexObject *object) { g_assert (object != NULL); g_assert (object->ref_count == 0); #ifdef HAVE_SYSPROF DEX_PROFILER_MARK (0, DEX_OBJECT_TYPE_NAME (object), "dex_object_finalize()"); DEX_PROFILER_MARK (SYSPROF_CAPTURE_CURRENT_TIME - object->ctime, DEX_OBJECT_TYPE_NAME (object), "lifetime"); #endif g_type_free_instance ((GTypeInstance *)object); } /** * dex_ref: (method) * @object: (type DexObject): the object to reference * * Acquires a reference on the given object, and increases its reference count by one. * * Returns: (transfer full) (type DexObject): the object with its reference count increased */ gpointer dex_ref (gpointer object) { DexObject *self = object; atomic_fetch_add_explicit (&self->ref_count, 1, memory_order_relaxed); return object; } /** * dex_unref: (method) * @object: (type DexObject) (transfer full): the object to unreference * * Releases a reference on the given object, and decreases its reference count by one. * * If it was the last reference, the resources associated to the instance are freed. */ void dex_unref (gpointer object) { DexObject *obj = object; DexObjectClass *object_class; DexWeakRef *weak_refs; guint watermark; g_return_if_fail (object != NULL); g_return_if_fail (DEX_IS_OBJECT (object)); /* Fetch a watermark before we decrement so that we can be * sure that if we reached zero, that anything that extended * the life of the mem_block will be responsible to free * the object in the end. */ watermark = g_atomic_int_get (&obj->weak_refs_watermark); /* If we decrement and it's not zero, then there is nothing * for this thread to do. Fast path. */ if G_LIKELY (atomic_fetch_sub_explicit (&obj->ref_count, 1, memory_order_release) != 1) return; atomic_thread_fence (memory_order_acquire); object_class = DEX_OBJECT_GET_CLASS (object); /* We reached zero. We need to go through our weak references and * acquire each of their mutexes so that we can be sure none of them * have raced against us to increment the value beyond 0. If they did, * then we can bail early and ignore any request to finalize. * * If the watermark is beyond ours, then we know that the weak-ref * extended liveness beyond this call and we are not responsible * for finalizing this object. */ dex_object_lock (object); for (DexWeakRef *wr = obj->weak_refs; wr; wr = wr->next) g_mutex_lock (&wr->mutex); /* If we increased our reference count on another thread by extending the * lifetime of the mem_block using a weak_ref-to-full_ref, then we don't need * to do anything here. Just bail and move along allowing a future unref to * finalize when it once again approaches zero. */ if (g_atomic_int_get (&obj->ref_count) > 0 || g_atomic_int_get (&obj->weak_refs_watermark) != watermark) { for (DexWeakRef *wr = obj->weak_refs; wr; wr = wr->next) g_mutex_unlock (&wr->mutex); dex_object_unlock (object); return; } weak_refs = g_steal_pointer (&obj->weak_refs); /* So we have locks on everything we can, we still are at zero, so that * means we need to zero out all our weak refs and then finalize the * object using the provided @clear_func. */ while (weak_refs != NULL) { DexWeakRef *wr = weak_refs; weak_refs = weak_refs->next; wr->prev = NULL; wr->next = NULL; wr->mem_block = NULL; g_mutex_unlock (&wr->mutex); } dex_object_unlock (object); /* If we did not create an immortal ref, then we are safe to finalize */ if (g_atomic_int_get (&obj->ref_count) == 0) object_class->finalize (object); } static void dex_object_class_init (DexObjectClass *klass) { klass->finalize = dex_object_finalize; } static void dex_object_init (DexObject *self, DexObjectClass *object_class) { #ifdef HAVE_SYSPROF self->ctime = SYSPROF_CAPTURE_CURRENT_TIME; DEX_PROFILER_MARK (0, g_type_name (G_TYPE_FROM_CLASS (object_class)), "dex_object_init()"); #endif self->ref_count = 1; g_mutex_init (&self->mutex); self->weak_refs_watermark = 1; } static void dex_object_add_weak (gpointer mem_block, DexWeakRef *weak_ref) { DexObject *object = mem_block; g_assert (object != NULL); g_assert (weak_ref != NULL); g_assert (weak_ref->prev == NULL); g_assert (weak_ref->next == NULL); g_assert (weak_ref->mem_block == mem_block); /* Must own a full ref to acquire a weak ref */ g_return_if_fail (object->ref_count > 0); dex_object_lock (object); weak_ref->prev = NULL; weak_ref->next = object->weak_refs; if (object->weak_refs != NULL) object->weak_refs->prev = weak_ref; object->weak_refs = weak_ref; dex_object_unlock (object); } static void dex_object_remove_weak (gpointer mem_block, DexWeakRef *weak_ref) { DexObject *object = mem_block; g_assert (object != NULL); g_assert (weak_ref != NULL); /* Must own a full ref to release a weak ref */ g_return_if_fail (object->ref_count > 0); dex_object_lock (object); if (weak_ref->prev != NULL) weak_ref->prev->next = weak_ref->next; if (weak_ref->next != NULL) weak_ref->next->prev = weak_ref->prev; if (object->weak_refs == weak_ref) object->weak_refs = weak_ref->next; g_assert (object->weak_refs == NULL || object->weak_refs->prev == NULL); weak_ref->next = NULL; weak_ref->prev = NULL; weak_ref->mem_block = NULL; dex_object_unlock (object); } /** * dex_weak_ref_init: (skip) * @weak_ref: (out caller-allocates): uninitialized memory to store a weak ref * @mem_block: (nullable): the mem_block weak reference * * Creates a new weak reference to @mem_block. * * @mem_block must be a type that is created with dex_object_alloc0(), * dex_object_new0(), or similar; otherwise %NULL. * * It is an error to create a new weak reference after @mem_block has * started disposing which in practice means you must own a full reference * to create a weak reference. */ void dex_weak_ref_init (DexWeakRef *weak_ref, gpointer mem_block) { g_return_if_fail (weak_ref != NULL); g_return_if_fail (!mem_block || DEX_IS_OBJECT (mem_block)); g_return_if_fail (!mem_block || DEX_OBJECT (mem_block)->ref_count > 0); memset (weak_ref, 0, sizeof *weak_ref); g_mutex_init (&weak_ref->mutex); if (mem_block) dex_weak_ref_set (weak_ref, mem_block); } static inline gpointer dex_weak_ref_get_locked (DexWeakRef *weak_ref) { if (weak_ref->mem_block != NULL) { guint watermark; /* We have a pointer to our mem_block still. That means either the * object has a reference count greater-than zero, or we are running * against the finalizer (which must acquire weak_ref->mutex before it * can attempt to run finalizers). */ DexObject *object = weak_ref->mem_block; /* Increment the watermark so that any calls to dex_unref() racing * against us can detect that we extended liveness. If we have raced * G_MAXUINT32 times, then something nefarious is going on and we can * just make the object immortal. * * Otherwise, just add a single reference to own the object. */ watermark = g_atomic_int_add (&object->weak_refs_watermark, 1); atomic_fetch_add_explicit (&object->ref_count, 1 + (watermark == G_MAXUINT32), memory_order_relaxed); #ifdef HAVE_SYSPROF { char *message = g_strdup_printf ("%s@%p converted to full", DEX_OBJECT_TYPE_NAME (weak_ref->mem_block), weak_ref->mem_block); DEX_PROFILER_MARK (0, "DexWeakRef", message); g_free (message); } #endif return weak_ref->mem_block; } return NULL; } /** * dex_weak_ref_get: (skip) * @weak_ref: a #DexWeakRef * * Converts a weak ref into a full reference. * * This attempts to convert the #DexWeakRef created with * dex_weak_ref_init() into a full reference. * * If the mem_block pointed to by @weak_ref has already been released, or * is racing against disposal, %NULL is returned. * * Returns: (transfer full) (nullable): the mem_block or %NULL */ gpointer dex_weak_ref_get (DexWeakRef *weak_ref) { gpointer ret; g_return_val_if_fail (weak_ref != NULL, NULL); g_mutex_lock (&weak_ref->mutex); ret = dex_weak_ref_get_locked (weak_ref); g_mutex_unlock (&weak_ref->mutex); return ret; } /** * dex_weak_ref_clear: (skip) * @weak_ref: a #DexWeakRef * * Clears a #DexWeakRef that was previous registered with a mem_block * using dex_weak_ref_init(). * * It is an error to call this method while other threads are accessing * the #DexWeakRef. */ void dex_weak_ref_clear (DexWeakRef *weak_ref) { gpointer mem_block; g_return_if_fail (weak_ref != NULL); /* To detach a weak ref, you MUST own a full reference first. If we * fail to acquire a full reference, then our weak ref has been * abandoned by the mem_block and we are free to clean it up by * clearing our mutex. */ mem_block = dex_weak_ref_get (weak_ref); if (mem_block != NULL) { dex_object_remove_weak (mem_block, weak_ref); dex_clear (&mem_block); } /* These should be NULL because they've been removed above, or were * already removed during finalization. */ g_assert (weak_ref->prev == NULL); g_assert (weak_ref->next == NULL); g_assert (weak_ref->mem_block == NULL); g_mutex_clear (&weak_ref->mutex); } /** * dex_weak_ref_set: (skip) * @weak_ref: a #DexWeakRef * @mem_block: (nullable): the mem_block or %NULL * * Sets a #DexWeakRef to @mem_block. * * @mem_block must be a type allocated with dex_object_alloc0() or * equivalent allocator. * * It is an error to call this method without a full reference to * @mem_block. */ void dex_weak_ref_set (DexWeakRef *weak_ref, gpointer mem_block) { gpointer old_mem_block; g_return_if_fail (weak_ref != NULL); g_return_if_fail (!mem_block || DEX_IS_OBJECT (mem_block)); g_return_if_fail (!mem_block || DEX_OBJECT (mem_block)->ref_count > 0); g_mutex_lock (&weak_ref->mutex); old_mem_block = dex_weak_ref_get_locked (weak_ref); if (old_mem_block != mem_block) { if (old_mem_block != NULL) dex_object_remove_weak (old_mem_block, weak_ref); weak_ref->mem_block = mem_block; if (mem_block != NULL) dex_object_add_weak (mem_block, weak_ref); } g_mutex_unlock (&weak_ref->mutex); dex_clear (&old_mem_block); } static void value_init (GValue *value) { value->data[0].v_pointer = NULL; } static void value_free_value (GValue *value) { dex_clear (&value->data[0].v_pointer); } static void value_copy_value (const GValue *src, GValue *dst) { if (src->data[0].v_pointer != NULL) dst->data[0].v_pointer = dex_ref (src->data[0].v_pointer); else dst->data[0].v_pointer = NULL; } static gpointer value_peek_pointer (const GValue *value) { return value->data[0].v_pointer; } static char * value_collect_value (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { DexObject *object = collect_values[0].v_pointer; if (object == NULL) { value->data[0].v_pointer = NULL; return NULL; } if (object->parent_instance.g_class == NULL) return g_strconcat ("invalid unclassed DexObject pointer for value type '", G_VALUE_TYPE_NAME (value), "'", NULL); value->data[0].v_pointer = dex_ref (object); return NULL; } static char * value_lcopy_value (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { const DexObject **object_p = collect_values[0].v_pointer; if G_UNLIKELY (object_p == NULL) return g_strconcat ("value location for '", G_VALUE_TYPE_NAME (value), "' passed as NULL", NULL); if (value->data[0].v_pointer == NULL) *object_p = NULL; else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) *object_p = value->data[0].v_pointer; else *object_p = dex_ref (value->data[0].v_pointer); return NULL; } GType dex_object_get_type (void) { if (g_once_init_enter (&dex_object_type)) { GType gtype = g_type_register_fundamental (g_type_fundamental_next (), g_intern_static_string ("DexObject"), &(const GTypeInfo) { sizeof (DexObjectClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) dex_object_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (DexObject), 0, (GInstanceInitFunc) dex_object_init, /* GValue */ &(const GTypeValueTable) { value_init, value_free_value, value_copy_value, value_peek_pointer, "p", value_collect_value, "p", value_lcopy_value, }, }, &(const GTypeFundamentalInfo) { (G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE), }, G_TYPE_FLAG_ABSTRACT); g_assert (gtype != G_TYPE_INVALID); g_once_init_leave (&dex_object_type, gtype); } return dex_object_type; } DexObject * dex_object_create_instance (GType instance_type) { return (DexObject *)(gpointer)g_type_create_instance (instance_type); } /** * dex_value_get_object: * @value: a `GValue` initialized with type `DEX_TYPE_OBJECT` * * Retrieves the `DexObject` stored inside the given `value`. * * Returns: (transfer none) (nullable): a `DexObject` * * Since: 0.4 */ DexObject * dex_value_get_object (const GValue *value) { g_return_val_if_fail (G_VALUE_HOLDS (value, DEX_TYPE_OBJECT), NULL); return value->data[0].v_pointer; } /** * dex_value_set_object: * @value: a [struct@GObject.Value] initialized with type `DEX_TYPE_OBJECT` * @object: (nullable): a `DexObject` or %NULL * * Stores the given `DexObject` inside `value`. * * The [struct@GObject.Value] will acquire a reference to the `object`. * * Since: 0.4 */ void dex_value_set_object (GValue *value, DexObject *object) { if (object != NULL) dex_ref (object); dex_value_take_object (value, object); } /** * dex_value_take_object: * @value: a [struct@GObject.Value] initialized with type `DEX_TYPE_OBJECT` * @object: (transfer full) (nullable): a `DexObject` * * Stores the given `DexObject` inside `value`. * * This function transfers the ownership of the `object` to the `GValue`. * * Since: 0.4 */ void dex_value_take_object (GValue *value, DexObject *object) { DexObject *old_object; g_return_if_fail (G_VALUE_HOLDS (value, DEX_TYPE_OBJECT)); old_object = value->data[0].v_pointer; if (object != NULL) { g_return_if_fail (DEX_IS_OBJECT (object)); value->data[0].v_pointer = object; } else { value->data[0].v_pointer = NULL; } if (old_object != NULL) dex_unref (old_object); } 0707010000004D000081A40000000000000000000000016712D33C00000856000000000000000000000000000000000000001E00000000libdex-0.8.1/src/dex-object.h/* * dex-object.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib-object.h> #include "dex-version-macros.h" G_BEGIN_DECLS #define DEX_TYPE_OBJECT (dex_object_get_type()) #define DEX_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_OBJECT, DexObject)) #define DEX_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_OBJECT)) #define DEX_OBJECT_TYPE(obj) (G_TYPE_FROM_INSTANCE(obj)) #define DEX_OBJECT_TYPE_NAME(obj) (g_type_name(DEX_OBJECT_TYPE(obj))) typedef struct _DexObject DexObject; DEX_AVAILABLE_IN_ALL GType dex_object_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL gpointer dex_ref (gpointer object); DEX_AVAILABLE_IN_ALL void dex_unref (gpointer object); #ifndef __GI_SCANNER__ static inline void dex_clear (gpointer data) { DexObject **objptr = (DexObject **)data; DexObject *obj = *objptr; *objptr = NULL; if (obj != NULL) dex_unref (obj); } #endif DEX_AVAILABLE_IN_ALL DexObject *dex_value_get_object (const GValue *value); DEX_AVAILABLE_IN_ALL void dex_value_set_object (GValue *value, DexObject *object); DEX_AVAILABLE_IN_ALL void dex_value_take_object (GValue *value, DexObject *object); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexObject, dex_unref) G_END_DECLS 0707010000004E000081A40000000000000000000000016712D33C000008C3000000000000000000000000000000000000002000000000libdex-0.8.1/src/dex-platform.c/* dex-platform.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-platform.h" #ifdef G_OS_WIN32 # include <windows.h> #else # include <unistd.h> #endif gsize dex_get_page_size (void) { static gsize page_size; if G_UNLIKELY (page_size == 0) { #ifdef G_OS_WIN32 SYSTEM_INFO si; GetSystemInfo (&si); page_size = si.dwPageSize; #else page_size = sysconf (_SC_PAGESIZE); #endif } return page_size; } gsize dex_get_min_stack_size (void) { static gsize min_stack_size; if G_UNLIKELY (min_stack_size == 0) { #ifdef G_OS_WIN32 /* Probably need to base this on granularity or something, * because the default stack size of 1MB is likely too much. */ min_stack_size = 4096*16; #else long sc_thread_stack_min; /* On FreeBSD we can get 2048 for min-stack-size which * doesn't really work well for pretty much anything. * Even when we round up to 4096 we trivially hit the * guard page. Perhaps something still needs to be * fixed in DexStack to ensure we don't write somewhere * we shouldn't, but the easiest thing to do right now * is to just enforce 2+pages + guard page. */ min_stack_size = dex_get_page_size () * 2; sc_thread_stack_min = sysconf (_SC_THREAD_STACK_MIN); if (sc_thread_stack_min != -1 && sc_thread_stack_min > min_stack_size) min_stack_size = sc_thread_stack_min; #endif } return min_stack_size; } 0707010000004F000081A40000000000000000000000016712D33C000003F6000000000000000000000000000000000000002000000000libdex-0.8.1/src/dex-platform.h/* dex-platform.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-version-macros.h" G_BEGIN_DECLS DEX_AVAILABLE_IN_ALL gsize dex_get_page_size (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL gsize dex_get_min_stack_size (void) G_GNUC_CONST; G_END_DECLS 07070100000050000081A40000000000000000000000016712D33C000006BE000000000000000000000000000000000000003100000000libdex-0.8.1/src/dex-posix-aio-backend-private.h/* dex-posix-aio-backend-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-aio-backend-private.h" G_BEGIN_DECLS #define DEX_TYPE_POSIX_AIO_BACKEND (dex_posix_aio_backend_get_type()) #define DEX_POSIX_AIO_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_POSIX_AIO_BACKEND, DexPosixAioBackend)) #define DEX_IS_POSIX_AIO_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_POSIX_AIO_BACKEND)) typedef struct _DexPosixAioBackend DexPosixAioBackend; typedef struct _DexPosixAioContext DexPosixAioContext; typedef struct _DexPosixAioFuture DexPosixAioFuture; typedef struct _DexPosixAioBackendClass DexPosixAioBackendClass; GType dex_posix_aio_backend_get_type (void) G_GNUC_CONST; DexAioBackend *dex_posix_aio_backend_new (void); void dex_posix_aio_context_enqueue (DexPosixAioContext *posix_aio_context, DexPosixAioFuture *posix_aio_future); G_END_DECLS 07070100000051000081A40000000000000000000000016712D33C00001EAD000000000000000000000000000000000000002900000000libdex-0.8.1/src/dex-posix-aio-backend.c/* dex-posix-aio-backend.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <errno.h> #include <fcntl.h> #include <unistd.h> #include "dex-compat-private.h" #include "dex-posix-aio-backend-private.h" #include "dex-posix-aio-future-private.h" #define N_IO_WORKERS 8 /* * The DexPosixAioBackend uses synchronous IO on a worker threads for * read()/write() as we don't have a reliable poll()-style interface for * doing IO on regular files. epoll_create() would be an option except * that on Linux it doesn't guarantee support for regular files to * return EAGAIN properly. * * If/when we introduce AIO operations for send()/recv() then those would * use g_source_add_unix_fd()/g_source_remove_unix_fd() to poll() within * the GMainContext. * * This is primarily meant to be a fallback for cases where we cannot * support more specific APIs like io_posix or kqueue. */ struct _DexPosixAioBackend { DexAioBackend parent_instance; }; struct _DexPosixAioBackendClass { DexAioBackendClass parent_class; }; typedef struct _DexPosixAioContext { DexAioContext parent; GMutex mutex; GQueue completed; } DexPosixAioContext; DEX_DEFINE_FINAL_TYPE (DexPosixAioBackend, dex_posix_aio_backend, DEX_TYPE_AIO_BACKEND) static GThreadPool *io_thread_pool; static void dex_posix_aio_context_take (DexPosixAioContext *posix_aio_context, DexPosixAioFuture *posix_aio_future) { GMainContext *main_context; g_assert (posix_aio_context != NULL); g_assert (DEX_IS_POSIX_AIO_FUTURE (posix_aio_future)); g_mutex_lock (&posix_aio_context->mutex); g_queue_push_tail (&posix_aio_context->completed, posix_aio_future); main_context = dex_posix_aio_future_get_main_context (posix_aio_future); g_mutex_unlock (&posix_aio_context->mutex); if (main_context) g_main_context_wakeup (main_context); } static inline void steal_queue (GQueue *from_queue, GQueue *to_queue) { *to_queue = *from_queue; *from_queue = (GQueue) {NULL, NULL, 0}; } static gboolean dex_posix_aio_context_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { DexPosixAioContext *aio_context = (DexPosixAioContext *)source; GQueue completed; g_mutex_lock (&aio_context->mutex); steal_queue (&aio_context->completed, &completed); g_mutex_unlock (&aio_context->mutex); while (completed.length > 0) { DexPosixAioFuture *posix_aio_future = g_queue_pop_head (&completed); dex_posix_aio_future_complete (posix_aio_future); dex_unref (posix_aio_future); } return G_SOURCE_CONTINUE; } static gboolean dex_posix_aio_context_check (GSource *source) { DexPosixAioContext *aio_context = (DexPosixAioContext *)source; gboolean ret; g_assert (aio_context != NULL); g_assert (DEX_IS_POSIX_AIO_BACKEND (aio_context->parent.aio_backend)); g_mutex_lock (&aio_context->mutex); ret = aio_context->completed.length > 0; g_mutex_unlock (&aio_context->mutex); return ret; } static gboolean dex_posix_aio_context_prepare (GSource *source, int *timeout) { *timeout = -1; return dex_posix_aio_context_check (source); } static void dex_posix_aio_context_finalize (GSource *source) { DexPosixAioContext *aio_context = (DexPosixAioContext *)source; g_assert (aio_context != NULL); g_assert (DEX_IS_POSIX_AIO_BACKEND (aio_context->parent.aio_backend)); g_assert (aio_context->completed.length == 0); g_mutex_clear (&aio_context->mutex); } static GSourceFuncs dex_posix_aio_context_source_funcs = { .check = dex_posix_aio_context_check, .prepare = dex_posix_aio_context_prepare, .dispatch = dex_posix_aio_context_dispatch, .finalize = dex_posix_aio_context_finalize, }; static DexAioContext * dex_posix_aio_backend_create_context (DexAioBackend *aio_backend) { DexPosixAioContext *aio_context; g_assert (DEX_IS_POSIX_AIO_BACKEND (aio_backend)); aio_context = (DexPosixAioContext *) g_source_new (&dex_posix_aio_context_source_funcs, sizeof *aio_context); _g_source_set_static_name ((GSource *)aio_context, "[dex-posix-aio-backend]"); g_source_set_can_recurse ((GSource *)aio_context, TRUE); aio_context->parent.aio_backend = dex_ref (aio_backend); g_mutex_init (&aio_context->mutex); return (DexAioContext *)aio_context; } static DexFuture * dex_posix_aio_backend_read (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gpointer buffer, gsize count, goffset offset) { DexPosixAioFuture *posix_aio_future; posix_aio_future = dex_posix_aio_future_new_read ((DexPosixAioContext *)aio_context, fd, buffer, count, offset); g_thread_pool_push (io_thread_pool, dex_ref (posix_aio_future), NULL); return DEX_FUTURE (posix_aio_future); } static DexFuture * dex_posix_aio_backend_write (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gconstpointer buffer, gsize count, goffset offset) { DexPosixAioFuture *posix_aio_future; posix_aio_future = dex_posix_aio_future_new_write ((DexPosixAioContext *)aio_context, fd, buffer, count, offset); g_thread_pool_push (io_thread_pool, dex_ref (posix_aio_future), NULL); return DEX_FUTURE (posix_aio_future); } static void dex_posix_aio_backend_worker (gpointer data, gpointer user_data) { DexPosixAioFuture *posix_aio_future = data; DexPosixAioContext *posix_aio_context; g_assert (DEX_IS_POSIX_AIO_FUTURE (posix_aio_future)); posix_aio_context = dex_posix_aio_future_get_aio_context (posix_aio_future); dex_posix_aio_future_run (posix_aio_future); dex_posix_aio_context_take (posix_aio_context, posix_aio_future); } static void dex_posix_aio_backend_class_init (DexPosixAioBackendClass *posix_aio_backend_class) { DexAioBackendClass *aio_backend_class = DEX_AIO_BACKEND_CLASS (posix_aio_backend_class); GError *error = NULL; aio_backend_class->create_context = dex_posix_aio_backend_create_context; aio_backend_class->read = dex_posix_aio_backend_read; aio_backend_class->write = dex_posix_aio_backend_write; io_thread_pool = g_thread_pool_new (dex_posix_aio_backend_worker, NULL, N_IO_WORKERS, FALSE, &error); if (io_thread_pool == NULL) g_error ("Failed to create thread pool: %s", error->message); g_type_ensure (DEX_TYPE_POSIX_AIO_FUTURE); } static void dex_posix_aio_backend_init (DexPosixAioBackend *posix_aio_backend) { } DexAioBackend * dex_posix_aio_backend_new (void) { return (DexAioBackend *)dex_object_create_instance (DEX_TYPE_POSIX_AIO_BACKEND); } 07070100000052000081A40000000000000000000000016712D33C000009F1000000000000000000000000000000000000003000000000libdex-0.8.1/src/dex-posix-aio-future-private.h/* dex-posix-aio-future-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-aio-backend-private.h" G_BEGIN_DECLS #define DEX_TYPE_POSIX_AIO_FUTURE (dex_posix_aio_future_get_type()) #define DEX_POSIX_AIO_FUTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_POSIX_AIO_FUTURE, DexPosixAioFuture)) #define DEX_IS_POSIX_AIO_FUTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_POSIX_AIO_FUTURE)) GType dex_posix_aio_future_get_type (void) G_GNUC_CONST; DexPosixAioFuture *dex_posix_aio_future_new_read (DexPosixAioContext *posix_aio_context, int fd, gpointer buffer, gsize count, goffset offset); DexPosixAioFuture *dex_posix_aio_future_new_write (DexPosixAioContext *posix_aio_context, int fd, gconstpointer buffer, gsize count, goffset offset); void dex_posix_aio_future_run (DexPosixAioFuture *posix_aio_future); void dex_posix_aio_future_complete (DexPosixAioFuture *posix_aio_future); DexPosixAioContext *dex_posix_aio_future_get_aio_context (DexPosixAioFuture *posix_aio_future); GMainContext *dex_posix_aio_future_get_main_context (DexPosixAioFuture *posix_aio_future); G_END_DECLS 07070100000053000081A40000000000000000000000016712D33C00001E86000000000000000000000000000000000000002800000000libdex-0.8.1/src/dex-posix-aio-future.c/* dex-posix-aio-future.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <errno.h> #include <unistd.h> #include <gio/gio.h> #include "dex-future-private.h" #include "dex-posix-aio-backend-private.h" #include "dex-posix-aio-future-private.h" typedef enum _DexPosixAioFutureKind { DEX_POSIX_AIO_FUTURE_READ = 1, DEX_POSIX_AIO_FUTURE_WRITE, } DexPosixAioFutureKind; struct _DexPosixAioFuture { DexFuture parent_instance; GMainContext *main_context; DexPosixAioContext *aio_context; DexPosixAioFutureKind kind; int errsv; union { struct { int fd; gpointer buffer; gsize count; goffset offset; gssize res; } read; struct { int fd; gconstpointer buffer; gsize count; goffset offset; gssize res; } write; }; }; typedef struct _DexPosixAioFutureClass { DexFutureClass parent_class; } DexPosixAioFutureClass; DEX_DEFINE_FINAL_TYPE (DexPosixAioFuture, dex_posix_aio_future, DEX_TYPE_FUTURE) #undef DEX_TYPE_POSIX_AIO_FUTURE #define DEX_TYPE_POSIX_AIO_FUTURE dex_posix_aio_future_type static void dex_posix_aio_future_finalize (DexObject *object) { DexPosixAioFuture *posix_aio_future = DEX_POSIX_AIO_FUTURE (object); g_clear_pointer ((GSource **)&posix_aio_future->aio_context, g_source_unref); g_clear_pointer (&posix_aio_future->main_context, g_main_context_unref); DEX_OBJECT_CLASS (dex_posix_aio_future_parent_class)->finalize (object); } static void dex_posix_aio_future_class_init (DexPosixAioFutureClass *posix_aio_future_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (posix_aio_future_class); object_class->finalize = dex_posix_aio_future_finalize; } static void dex_posix_aio_future_init (DexPosixAioFuture *posix_aio_future) { } static DexPosixAioFuture * dex_posix_aio_future_new (DexPosixAioFutureKind kind, DexPosixAioContext *aio_context) { DexPosixAioFuture *posix_aio_future; GMainContext *main_context; if ((main_context = g_source_get_context ((GSource *)aio_context))) g_main_context_ref (main_context); posix_aio_future = (DexPosixAioFuture *)dex_object_create_instance (DEX_TYPE_POSIX_AIO_FUTURE); posix_aio_future->kind = kind; posix_aio_future->aio_context = (DexPosixAioContext *)g_source_ref ((GSource *)aio_context); posix_aio_future->main_context = main_context; return posix_aio_future; } DexPosixAioFuture * dex_posix_aio_future_new_read (DexPosixAioContext *posix_aio_context, int fd, gpointer buffer, gsize count, goffset offset) { DexPosixAioFuture *posix_aio_future; posix_aio_future = dex_posix_aio_future_new (DEX_POSIX_AIO_FUTURE_READ, posix_aio_context); posix_aio_future->read.fd = fd; posix_aio_future->read.buffer = buffer; posix_aio_future->read.count = count; posix_aio_future->read.offset = offset; posix_aio_future->read.res = -1; return posix_aio_future; } DexPosixAioFuture * dex_posix_aio_future_new_write (DexPosixAioContext *posix_aio_context, int fd, gconstpointer buffer, gsize count, goffset offset) { DexPosixAioFuture *posix_aio_future; posix_aio_future = dex_posix_aio_future_new (DEX_POSIX_AIO_FUTURE_WRITE, posix_aio_context); posix_aio_future->write.fd = fd; posix_aio_future->write.buffer = buffer; posix_aio_future->write.count = count; posix_aio_future->write.offset = offset; posix_aio_future->write.res = -1; return posix_aio_future; } void dex_posix_aio_future_run (DexPosixAioFuture *posix_aio_future) { g_return_if_fail (DEX_IS_POSIX_AIO_FUTURE (posix_aio_future)); errno = 0; switch (posix_aio_future->kind) { case DEX_POSIX_AIO_FUTURE_READ: if (posix_aio_future->read.offset >= 0) posix_aio_future->read.res = pread (posix_aio_future->read.fd, posix_aio_future->read.buffer, posix_aio_future->read.count, posix_aio_future->read.offset); else posix_aio_future->read.res = read (posix_aio_future->read.fd, posix_aio_future->read.buffer, posix_aio_future->read.count); /* Silence unused-result */ (void)posix_aio_future->read.res; break; case DEX_POSIX_AIO_FUTURE_WRITE: if (posix_aio_future->write.offset >= 0) posix_aio_future->write.res = pwrite (posix_aio_future->write.fd, posix_aio_future->write.buffer, posix_aio_future->write.count, posix_aio_future->write.offset); else posix_aio_future->write.res = write (posix_aio_future->write.fd, posix_aio_future->write.buffer, posix_aio_future->write.count); /* Silence unused-result */ (void)posix_aio_future->write.res; break; default: g_assert_not_reached (); } posix_aio_future->errsv = errno; } static void dex_posix_aio_future_complete_int64 (DexPosixAioFuture *posix_aio_future, gint64 res) { g_assert (DEX_IS_POSIX_AIO_FUTURE (posix_aio_future)); if (res < 0) dex_future_complete (DEX_FUTURE (posix_aio_future), NULL, g_error_new_literal (G_IO_ERROR, g_io_error_from_errno (posix_aio_future->errsv), g_strerror (posix_aio_future->errsv))); else dex_future_complete (DEX_FUTURE (posix_aio_future), &(GValue) { G_TYPE_INT64, {{.v_int64 = res}}}, NULL); } void dex_posix_aio_future_complete (DexPosixAioFuture *posix_aio_future) { g_return_if_fail (DEX_IS_POSIX_AIO_FUTURE (posix_aio_future)); switch (posix_aio_future->kind) { case DEX_POSIX_AIO_FUTURE_READ: dex_posix_aio_future_complete_int64 (posix_aio_future, posix_aio_future->read.res); break; case DEX_POSIX_AIO_FUTURE_WRITE: dex_posix_aio_future_complete_int64 (posix_aio_future, posix_aio_future->write.res); break; default: g_assert_not_reached (); } } DexPosixAioContext * dex_posix_aio_future_get_aio_context (DexPosixAioFuture *posix_aio_future) { g_return_val_if_fail (DEX_IS_POSIX_AIO_FUTURE (posix_aio_future), NULL); return posix_aio_future->aio_context; } GMainContext * dex_posix_aio_future_get_main_context (DexPosixAioFuture *posix_aio_future) { g_return_val_if_fail (DEX_IS_POSIX_AIO_FUTURE (posix_aio_future), NULL); return posix_aio_future->main_context; } 07070100000054000081A40000000000000000000000016712D33C00000939000000000000000000000000000000000000002000000000libdex-0.8.1/src/dex-profiler.h/* dex-profiler.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> #ifndef PACKAGE_VERSION # error "config.h was not included before dex-profiler.h." #endif #ifdef HAVE_SYSPROF # include <sysprof-capture.h> #endif G_BEGIN_DECLS #ifdef HAVE_SYSPROF # define DEX_PROFILER_ENABLED 1 # define DEX_PROFILER_CURRENT_TIME SYSPROF_CAPTURE_CURRENT_TIME # define DEX_PROFILER_ACTIVE (sysprof_collector_is_active()) # define DEX_PROFILER_BEGIN_MARK \ G_STMT_START { \ gint64 __begin_time = SYSPROF_CAPTURE_CURRENT_TIME; # define DEX_PROFILER_END_MARK(name, message) \ G_STMT_START { \ gint64 __duration = SYSPROF_CAPTURE_CURRENT_TIME - __begin_time; \ sysprof_collector_mark (__begin_time, __duration, "libdex", name, message); \ } G_STMT_END; \ } G_STMT_END # define DEX_PROFILER_MARK(duration, name, message) \ G_STMT_START { \ sysprof_collector_mark (SYSPROF_CAPTURE_CURRENT_TIME - (duration), \ (duration), "libdex", name, message); \ } G_STMT_END # define DEX_PROFILER_LOG(format, ...) \ G_STMT_START { \ if (DEX_PROFILER_ACTIVE) \ sysprof_collector_log_printf(G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN, format, __VA_ARGS__); \ } G_STMT_END #else # undef DEX_PROFILER_ENABLED # define DEX_PROFILER_ACTIVE (0) # define DEX_PROFILER_CURRENT_TIME 0 # define DEX_PROFILER_MARK(duration, name, message) \ G_STMT_START { } G_STMT_END # define DEX_PROFILER_BEGIN_MARK G_STMT_START { # define DEX_PROFILER_END_MARK(name, message) (void)0; } G_STMT_END # define DEX_PROFILER_LOG(format, ...) G_STMT_START { } G_STMT_END #endif G_END_DECLS 07070100000055000081A40000000000000000000000016712D33C00001D35000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-promise.c/* * dex-promise.c * * Copyright 2022-2023 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-future-private.h" #include "dex-promise.h" /** * DexPromise: * * #DexPromise is a convenient #DexFuture for prpoagating a result or * rejection in appliction and library code. * * Use this when there is not a more specialized #DexFuture for your needs to * propagate a result or rejection to the caller in an asynchronous fashion. */ typedef struct _DexPromise { DexFuture parent_instance; GCancellable *cancellable; } DexPromise; typedef struct _DexPromiseClass { DexFutureClass parent_class; } DexPromiseClass; DEX_DEFINE_FINAL_TYPE (DexPromise, dex_promise, DEX_TYPE_FUTURE) #undef DEX_TYPE_PROMISE #define DEX_TYPE_PROMISE dex_promise_type static void dex_promise_discard (DexFuture *future) { DexPromise *promise = DEX_PROMISE (future); g_cancellable_cancel (promise->cancellable); } static void dex_promise_finalize (DexObject *object) { DexPromise *self = (DexPromise *)object; g_clear_object (&self->cancellable); DEX_OBJECT_CLASS (dex_promise_parent_class)->finalize (object); } static void dex_promise_class_init (DexPromiseClass *promise_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (promise_class); DexFutureClass *future_class = DEX_FUTURE_CLASS (promise_class); object_class->finalize = dex_promise_finalize; future_class->discard = dex_promise_discard; } static void dex_promise_init (DexPromise *promise) { } DexPromise * (dex_promise_new) (void) { return (DexPromise *)dex_object_create_instance (dex_promise_type); } /** * dex_promise_new_cancellable: * * Creates a new #DexPromise that can propagate cancellation if the * promise is discarded. * * This can be used to plumb cancellation between promises and * #GAsyncReadyCallback based APIs. * * Returns: (transfer full): a #DexPromise */ DexPromise * (dex_promise_new_cancellable) (void) { DexPromise *self = (DexPromise *)dex_object_create_instance (dex_promise_type); self->cancellable = g_cancellable_new (); return self; } /** * dex_promise_get_cancellable: * @promise: a #DexPromise * * Gets a #GCancellable that will cancel when the promise has * been discarded (and therefore result no longer necessary). * * This is useful when manually implementing wrappers around various * #GAsyncReadyCallback based API. * * If @promise was created with dex_promise_new(), then %NULL is returned. * * Returns: (transfer none) (nullable): a #GCancellable or %NULL */ GCancellable * dex_promise_get_cancellable (DexPromise *promise) { g_return_val_if_fail (DEX_IS_PROMISE (promise), NULL); return promise->cancellable; } /** * dex_promise_resolve: * @promise: a #DexPromise * @value: a #GValue containing the resolved value * * Sets the result for a #DexPromise. */ void dex_promise_resolve (DexPromise *promise, const GValue *value) { g_return_if_fail (DEX_IS_PROMISE (promise)); g_return_if_fail (value != NULL && G_IS_VALUE (value)); dex_future_complete (DEX_FUTURE (promise), value, NULL); } /** * dex_promise_reject: * @promise: a #DexPromise * @error: (transfer full): a #GError * * Marks the promise as rejected, indicating a failure. */ void dex_promise_reject (DexPromise *promise, GError *error) { g_return_if_fail (DEX_IS_PROMISE (promise)); g_return_if_fail (error != NULL); dex_future_complete (DEX_FUTURE (promise), NULL, error); } void dex_promise_resolve_int (DexPromise *promise, int value) { GValue gvalue = {G_TYPE_INT, {{.v_int = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_uint (DexPromise *promise, guint value) { GValue gvalue = {G_TYPE_UINT, {{.v_uint = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_int64 (DexPromise *promise, gint64 value) { GValue gvalue = {G_TYPE_INT64, {{.v_int64 = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_uint64 (DexPromise *promise, guint64 value) { GValue gvalue = {G_TYPE_UINT64, {{.v_int64 = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_long (DexPromise *promise, glong value) { GValue gvalue = {G_TYPE_LONG, {{.v_long = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_ulong (DexPromise *promise, glong value) { GValue gvalue = {G_TYPE_ULONG, {{.v_ulong = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_float (DexPromise *promise, float value) { GValue gvalue = {G_TYPE_FLOAT, {{.v_float = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_double (DexPromise *promise, double value) { GValue gvalue = {G_TYPE_DOUBLE, {{.v_double = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } void dex_promise_resolve_boolean (DexPromise *promise, gboolean value) { GValue gvalue = {G_TYPE_BOOLEAN, {{.v_int = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); } /** * dex_promise_resolve_string: * @promise: a #DexPromise * @value: (transfer full): a string to use to resolve the promise * */ void dex_promise_resolve_string (DexPromise *promise, char *value) { GValue gvalue = {G_TYPE_STRING, {{.v_pointer = value}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); g_free (value); } /** * dex_promise_resolve_object: * @promise: a #DexPromise * @object: (type GObject) (transfer full) (nullable): a #GObject * */ void dex_promise_resolve_object (DexPromise *promise, gpointer object) { GType gtype = object ? G_OBJECT_TYPE (object) : G_TYPE_OBJECT; GValue gvalue = {gtype, {{.v_pointer = object}, {.v_int = 0}}}; dex_promise_resolve (promise, &gvalue); g_clear_object (&object); } /** * dex_promise_resolve_variant: * @promise: a #DexPromise * @variant: (transfer full) (nullable): a #GVariant * * If @variant is floating, its reference is consumed. * * Since: 0.8 */ void dex_promise_resolve_variant (DexPromise *promise, GVariant *variant) { GValue gvalue = G_VALUE_INIT; g_value_init (&gvalue, G_TYPE_VARIANT); g_value_take_variant (&gvalue, variant); dex_promise_resolve (promise, &gvalue); g_value_unset (&gvalue); } 07070100000056000081A40000000000000000000000016712D33C00001025000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-promise.h/* * dex-promise.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined (DEX_INSIDE) && !defined (DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif #include <gio/gio.h> #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_PROMISE (dex_promise_get_type()) #define DEX_IS_PROMISE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_PROMISE)) #define DEX_PROMISE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_PROMISE, DexPromise)) typedef struct _DexPromise DexPromise; DEX_AVAILABLE_IN_ALL GType dex_promise_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexPromise *dex_promise_new (void) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexPromise *dex_promise_new_cancellable (void) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL GCancellable *dex_promise_get_cancellable (DexPromise *promise); DEX_AVAILABLE_IN_ALL void dex_promise_resolve (DexPromise *promise, const GValue *value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_int (DexPromise *promise, int value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_uint (DexPromise *promise, guint value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_int64 (DexPromise *promise, gint64 value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_uint64 (DexPromise *promise, guint64 value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_long (DexPromise *promise, glong value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_ulong (DexPromise *promise, glong value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_float (DexPromise *promise, float value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_double (DexPromise *promise, double value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_boolean (DexPromise *promise, gboolean value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_string (DexPromise *promise, char *value); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_object (DexPromise *promise, gpointer object); DEX_AVAILABLE_IN_ALL void dex_promise_resolve_variant (DexPromise *promise, GVariant *variant); DEX_AVAILABLE_IN_ALL void dex_promise_reject (DexPromise *promise, GError *error); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexPromise, dex_unref) #if G_GNUC_CHECK_VERSION(3,0) && defined(DEX_ENABLE_DEBUG) # define _DEX_PROMISE_NEW(func, ...) \ ({ DexPromise *__p = G_PASTE (dex_promise_, func) (__VA_ARGS__); \ dex_future_set_static_name (DEX_FUTURE (__p), G_STRLOC); \ __p; }) # define dex_promise_new() _DEX_PROMISE_NEW(new) #endif G_END_DECLS 07070100000057000081A40000000000000000000000016712D33C00000889000000000000000000000000000000000000002900000000libdex-0.8.1/src/dex-scheduler-private.h/* * dex-scheduler-private.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-aio-backend-private.h" #include "dex-fiber.h" #include "dex-object-private.h" #include "dex-scheduler.h" G_BEGIN_DECLS #define DEX_SCHEDULER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, DEX_TYPE_SCHEDULER, DexSchedulerClass)) #define DEX_SCHEDULER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, DEX_TYPE_SCHEDULER, DexSchedulerClass)) typedef struct _DexWorkItem { DexSchedulerFunc func; gpointer func_data; } DexWorkItem; typedef struct _DexScheduler { DexObject parent_instance; } DexScheduler; typedef struct _DexSchedulerClass { DexObjectClass parent_class; void (*push) (DexScheduler *scheduler, DexWorkItem work_item); void (*spawn) (DexScheduler *scheduler, DexFiber *fiber); GMainContext *(*get_main_context) (DexScheduler *scheduler); DexAioContext *(*get_aio_context) (DexScheduler *scheduler); } DexSchedulerClass; void dex_scheduler_set_thread_default (DexScheduler *scheduler); void dex_scheduler_set_default (DexScheduler *scheduler); DexAioContext *dex_scheduler_get_aio_context (DexScheduler *scheduler); static inline void dex_work_item_invoke (const DexWorkItem *work_item) { work_item->func (work_item->func_data); } G_END_DECLS 07070100000058000081A40000000000000000000000016712D33C00001800000000000000000000000000000000000000002100000000libdex-0.8.1/src/dex-scheduler.c/* * dex-scheduler.c * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-aio-backend-private.h" #include "dex-fiber-private.h" #include "dex-scheduler-private.h" #include "dex-thread-storage-private.h" /** * DexScheduler: * * #DexScheduler is the base class used by schedulers. * * Schedulers are responsible for ensuring asynchronous IO requests and * completions are processed. They also schedule closures to be run as part * of future result propagation. Additionally, they manage #DexFiber execution * and suspension. * * Specialized schedulers such as #DexThreadPoolScheduler will do this for a * number of threads and dispatch new work between them. */ static DexScheduler *default_scheduler; DEX_DEFINE_ABSTRACT_TYPE (DexScheduler, dex_scheduler, DEX_TYPE_OBJECT) #undef DEX_TYPE_SCHEDULER #define DEX_TYPE_SCHEDULER dex_scheduler_type static void dex_scheduler_class_init (DexSchedulerClass *scheduler_class) { } static void dex_scheduler_init (DexScheduler *scheduler) { } /** * dex_scheduler_get_default: * * Gets the default scheduler for the process. * * The default scheduler executes tasks within the default #GMainContext. * Typically that is the main thread of the application. * * Returns: (transfer none) (not nullable): a #DexScheduler */ DexScheduler * dex_scheduler_get_default (void) { return default_scheduler; } void dex_scheduler_set_default (DexScheduler *scheduler) { g_return_if_fail (default_scheduler == NULL); g_return_if_fail (scheduler != NULL); default_scheduler = scheduler; } /** * dex_scheduler_get_thread_default: * * Gets the default scheduler for the thread. * * Returns: (transfer none) (nullable): a #DexScheduler or %NULL */ DexScheduler * dex_scheduler_get_thread_default (void) { return dex_thread_storage_get ()->scheduler; } void dex_scheduler_set_thread_default (DexScheduler *scheduler) { dex_thread_storage_get ()->scheduler = scheduler; } /** * dex_scheduler_ref_thread_default: * * Gets the thread default scheduler with the reference count incremented. * * Returns: (transfer full) (nullable): a #DexScheduler or %NULL */ DexScheduler * dex_scheduler_ref_thread_default (void) { DexScheduler *scheduler = dex_scheduler_get_thread_default (); if (scheduler != NULL) return dex_ref (scheduler); return NULL; } /** * dex_scheduler_push: * @scheduler: a #DexScheduler * @func: (scope async): the function callback * @func_data: the closure data for @func * * Queues @func to run on @scheduler. */ void dex_scheduler_push (DexScheduler *scheduler, DexSchedulerFunc func, gpointer func_data) { g_return_if_fail (DEX_IS_SCHEDULER (scheduler)); g_return_if_fail (func != NULL); DEX_SCHEDULER_GET_CLASS (scheduler)->push (scheduler, (DexWorkItem) {func, func_data}); } /** * dex_scheduler_get_main_context: * @scheduler: a #DexScheduler * * Gets the default main context for a scheduler. * * This may be a different value depending on the calling thread. * * For example, calling this on the #DexThreadPoolScheduer from outside * a worker thread may result in getting a shared #GMainContext for the * process. * * However, calling from a worker thread may give you a #GMainContext * specifically for that thread. * * Returns: (transfer none): a #GMainContext */ GMainContext * dex_scheduler_get_main_context (DexScheduler *scheduler) { return DEX_SCHEDULER_GET_CLASS (scheduler)->get_main_context (scheduler); } /** * dex_scheduler_get_aio_context: (skip) * @scheduler: a #DexScheduler * * Gets a #DexAioContext for the scheduler. * * This context can be used to execute asyncronous operations within the * context of the scheduler. Generally this is done using asynchronous * operations and submission/completions managed by the threads scheduler. */ DexAioContext * dex_scheduler_get_aio_context (DexScheduler *scheduler) { return DEX_SCHEDULER_GET_CLASS (scheduler)->get_aio_context (scheduler); } /** * dex_scheduler_spawn: * @scheduler: (nullable): a #DexScheduler * @stack_size: stack size in bytes or 0 * @func: (scope notified) (closure func_data) (destroy func_data_destroy): a #DexFiberFunc * @func_data: closure data for @func * @func_data_destroy: closure notify for @func_data * * Request @scheduler to spawn a #DexFiber. * * The fiber will have its own stack and cooperatively schedules among other * fibers sharing the schaeduler. * * If @stack_size is 0, it will set to a sensible default. Otherwise, it is * rounded up to the nearest page size. * * Returns: (transfer full): a #DexFuture that will resolve or reject when * @func completes (or its resulting #DexFuture completes). */ DexFuture * dex_scheduler_spawn (DexScheduler *scheduler, gsize stack_size, DexFiberFunc func, gpointer func_data, GDestroyNotify func_data_destroy) { DexFiber *fiber; g_return_val_if_fail (!scheduler || DEX_IS_SCHEDULER (scheduler), NULL); g_return_val_if_fail (func != NULL, NULL); if (scheduler == NULL) scheduler = dex_scheduler_get_default (); fiber = dex_fiber_new (func, func_data, func_data_destroy, stack_size); DEX_SCHEDULER_GET_CLASS (scheduler)->spawn (scheduler, fiber); return DEX_FUTURE (fiber); } 07070100000059000081A40000000000000000000000016712D33C00000E0A000000000000000000000000000000000000002100000000libdex-0.8.1/src/dex-scheduler.h/* * dex-scheduler.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined (DEX_INSIDE) && !defined (DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_SCHEDULER (dex_scheduler_get_type()) #define DEX_SCHEDULER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_SCHEDULER, DexScheduler)) #define DEX_IS_SCHEDULER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_SCHEDULER)) typedef struct _DexScheduler DexScheduler; typedef void (*DexSchedulerFunc) (gpointer user_data); /** * DexFiberFunc: * * This function prototype is used for spawning fibers. A fiber * is a lightweight, cooperative-multitasking feature where the * fiber is given its own stack. The fiber runs until it reaches * a point of suspension (using `dex_await` or similar) or exits * the fiber. * * When suspended, the fiber is placed onto a queue until it is * runnable again. Once runnable, the fiber is scheduled to run * from within whatever scheduler it was created with. * * See `dex_scheduler_spawn()` * * Returns: (transfer full) (nullable): a #DexFuture or %NULL */ typedef DexFuture *(*DexFiberFunc) (gpointer user_data); DEX_AVAILABLE_IN_ALL GType dex_scheduler_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexScheduler *dex_scheduler_get_thread_default (void); DEX_AVAILABLE_IN_ALL DexScheduler *dex_scheduler_ref_thread_default (void) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL DexScheduler *dex_scheduler_get_default (void); DEX_AVAILABLE_IN_ALL GMainContext *dex_scheduler_get_main_context (DexScheduler *scheduler); DEX_AVAILABLE_IN_ALL void dex_scheduler_push (DexScheduler *scheduler, DexSchedulerFunc func, gpointer func_data); DEX_AVAILABLE_IN_ALL DexFuture *dex_scheduler_spawn (DexScheduler *scheduler, gsize stack_size, DexFiberFunc func, gpointer func_data, GDestroyNotify func_data_destroy) G_GNUC_WARN_UNUSED_RESULT; #if G_GNUC_CHECK_VERSION(3,0) && defined(DEX_ENABLE_DEBUG) # define _DEX_FIBER_NEW_(counter, ...) \ ({ DexFuture *G_PASTE(__f, counter) = dex_scheduler_spawn (__VA_ARGS__); \ dex_future_set_static_name (DEX_FUTURE (G_PASTE (__f, counter)), G_STRLOC); \ G_PASTE (__f, counter); }) # define _DEX_FIBER_NEW(...) _DEX_FIBER_NEW_(__COUNTER__, __VA_ARGS__) # define dex_scheduler_spawn(...) _DEX_FIBER_NEW(__VA_ARGS__) #endif G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexScheduler, dex_unref) G_END_DECLS 0707010000005A000081A40000000000000000000000016712D33C0000063E000000000000000000000000000000000000002900000000libdex-0.8.1/src/dex-semaphore-private.h/* * dex-semaphore-private.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_SEMAPHORE (dex_semaphore_get_type()) #define DEX_SEMAPHORE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_SEMAPHORE, DexSemaphore)) #define DEX_IS_SEMAPHORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_SEMAPHORE)) typedef struct _DexSemaphore DexSemaphore; GType dex_semaphore_get_type (void) G_GNUC_CONST; DexSemaphore *dex_semaphore_new (void); void dex_semaphore_post (DexSemaphore *semaphore); void dex_semaphore_post_many (DexSemaphore *semaphore, guint count); DexFuture *dex_semaphore_wait (DexSemaphore *semaphore); void dex_semaphore_close (DexSemaphore *semaphore); G_END_DECLS 0707010000005B000081A40000000000000000000000016712D33C000025D4000000000000000000000000000000000000002100000000libdex-0.8.1/src/dex-semaphore.c/* * dex-semaphore.c * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <errno.h> #include <unistd.h> #ifdef HAVE_EVENTFD # include <sys/eventfd.h> #endif #ifdef HAVE_LIBURING # include <liburing.h> #endif #include <gio/gio.h> #include "dex-aio.h" #include "dex-block-private.h" #include "dex-error.h" #include "dex-future-private.h" #include "dex-object-private.h" #include "dex-promise.h" #include "dex-semaphore-private.h" #include "dex-thread-storage-private.h" #include "dex-uring-aio-backend-private.h" /* * NOTES: * * The DexSemaphore class works in its ideal state by using an * eventfd() to notify other threads of work to be done using the * EFD_SEMPAHORE eventfd type. * * This is preferred to using a DexFuture waiter that is completed * when an item is posted because it uses less memory, doesn't require * queuing work on a thread, nor then waking up the specific waiter * thread using another eventfd from gwakeup.c. Additionally, all this * work can be completed in a single io_uring_submit() when running * on Linux. * * We will use the fallback case if we're not on io_uring because * otherwise we lock up our thread pool with blocking read(). * * For the fallback case, we use a future that is enqueued on the waiter * thread, and when a post comes in, we attempt to complete those. If * the waiter enqueues and items are already available, they will be * completed immediately. Since the waiter thread may be blocked in * poll(), or racing to block in poll(), we must wake up the GMainContext * so that it will process the completed future. DexBlock does this * automatically for us when processing the finally() block. Handling * things in this way has the additional benefit of working on Windows * should we get around to running there (even if we should use IoRing * there too). */ #define DEX_TYPE_SEMAPHORE_WAITER dex_semaphore_waiter_type typedef struct _DexSemaphoreWaiter { DexFuture parent_instance; GList link; } DexSemaphoreWaiter; typedef struct _DexSemaphoreWaiterClass { DexFutureClass parent_class; } DexSemaphoreWaiterClass; GType dex_semaphore_waiter_get_type (void) G_GNUC_CONST; DEX_DEFINE_FINAL_TYPE (DexSemaphoreWaiter, dex_semaphore_waiter, DEX_TYPE_FUTURE) static GError semaphore_closed_error; static GValue semaphore_waiter_value; static void dex_semaphore_waiter_class_init (DexSemaphoreWaiterClass *semaphore_waiter_class) { g_value_init (&semaphore_waiter_value, G_TYPE_BOOLEAN); g_value_set_boolean (&semaphore_waiter_value, TRUE); semaphore_closed_error = (GError) { .domain = DEX_ERROR, .code = DEX_ERROR_SEMAPHORE_CLOSED, .message = (char *)"Semaphore is closed", }; } static void dex_semaphore_waiter_init (DexSemaphoreWaiter *semaphore_waiter) { semaphore_waiter->link.data = semaphore_waiter; } struct _DexSemaphore { DexObject parent_instance; #ifdef HAVE_EVENTFD int eventfd; #endif gint64 counter; GQueue waiters; }; typedef struct _DexSemaphoreClass { DexObjectClass parent_class; } DexSemaphoreClass; DEX_DEFINE_FINAL_TYPE (DexSemaphore, dex_semaphore, DEX_TYPE_OBJECT) #undef DEX_TYPE_SEMAPHORE #define DEX_TYPE_SEMAPHORE dex_semaphore_type DexSemaphore * dex_semaphore_new (void) { return (DexSemaphore *)dex_object_create_instance (DEX_TYPE_SEMAPHORE); } static void dex_semaphore_finalize (DexObject *object) { DexSemaphore *semaphore = (DexSemaphore *)object; #ifdef HAVE_EVENTFD if (semaphore->eventfd != -1) { close (semaphore->eventfd); semaphore->eventfd = -1; } #else while (semaphore->waiters.length > 0) { DexSemaphoreWaiter *waiter = g_queue_pop_head_link (&semaphore->waiters)->data; dex_future_complete (DEX_FUTURE (waiter), NULL, g_error_copy (&semaphore_closed_error)); dex_unref (waiter); } #endif DEX_OBJECT_CLASS (dex_semaphore_parent_class)->finalize (object); } static void dex_semaphore_class_init (DexSemaphoreClass *semaphore_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (semaphore_class); object_class->finalize = dex_semaphore_finalize; g_type_ensure (dex_semaphore_waiter_get_type ()); } static void dex_semaphore_init (DexSemaphore *semaphore) { #ifdef HAVE_EVENTFD { /* We only support using eventfd if our AIO backend is * io_uring because the threadpool AIO backend could end * up saturating the threadpool and deadlocking. * * See #17 */ #ifdef HAVE_LIBURING DexAioBackend *backend = dex_aio_backend_get_default (); if (DEX_IS_URING_AIO_BACKEND (backend)) semaphore->eventfd = eventfd (0, EFD_SEMAPHORE); else #endif semaphore->eventfd = -1; } #endif } void dex_semaphore_post (DexSemaphore *semaphore) { return dex_semaphore_post_many (semaphore, 1); } void dex_semaphore_post_many (DexSemaphore *semaphore, guint count) { g_return_if_fail (DEX_IS_SEMAPHORE (semaphore)); if (count == 0) return; #ifdef HAVE_EVENTFD if (semaphore->eventfd != -1) { guint64 counter = count; /* Writes to eventfd are 64-bit integers and always atomic. Anything * other than sizeof(counter) indicates failure and we are not prepared * to handle that as it shouldn't happen. Just bail. */ if (write (semaphore->eventfd, &counter, sizeof counter) != sizeof counter) { int errsv = errno; g_error ("Failed to post semaphore counter: %s", g_strerror (errsv)); } } else #endif { GQueue queue = G_QUEUE_INIT; /* Post count and steal as many workers as we can complete * immediately which are waiting on a result. */ dex_object_lock (semaphore); semaphore->counter += count; while (semaphore->counter > 0 && semaphore->waiters.length > 0) { g_queue_push_tail_link (&queue, g_queue_pop_head_link (&semaphore->waiters)); semaphore->counter--; } dex_object_unlock (semaphore); /* Now complete the waiters outside our object lock */ while (queue.length > 0) { DexSemaphoreWaiter *waiter = g_queue_pop_head_link (&queue)->data; dex_future_complete (DEX_FUTURE (waiter), &semaphore_waiter_value, NULL); dex_unref (waiter); } } } static DexFuture * dex_semaphore_wait_on_scheduler (DexFuture *future, gpointer user_data) { return dex_ref (future); } DexFuture * dex_semaphore_wait (DexSemaphore *semaphore) { g_return_val_if_fail (DEX_IS_SEMAPHORE (semaphore), NULL); #ifdef HAVE_EVENTFD if (semaphore->eventfd != -1) { static gint64 trash_value; return dex_aio_read (NULL, semaphore->eventfd, &trash_value, sizeof trash_value, -1); } else #endif { DexSemaphoreWaiter *waiter; DexFuture *ret = NULL; waiter = (DexSemaphoreWaiter *) dex_object_create_instance (DEX_TYPE_SEMAPHORE_WAITER); dex_object_lock (semaphore); if (semaphore->counter > 0) { semaphore->counter--; dex_future_complete (DEX_FUTURE (waiter), &semaphore_waiter_value, NULL); ret = DEX_FUTURE (g_steal_pointer (&waiter)); } else { DexScheduler *scheduler = dex_scheduler_ref_thread_default (); DexFuture *block; g_assert (scheduler != NULL); g_assert (DEX_IS_SCHEDULER (scheduler)); block = dex_block_new (dex_ref (waiter), scheduler, DEX_BLOCK_KIND_FINALLY, dex_semaphore_wait_on_scheduler, NULL, NULL); g_queue_push_tail_link (&semaphore->waiters, &waiter->link); ret = DEX_FUTURE (g_steal_pointer (&block)); dex_unref (scheduler); } dex_object_unlock (semaphore); g_assert (ret != NULL); g_assert (DEX_IS_FUTURE (ret)); return ret; } } void dex_semaphore_close (DexSemaphore *semaphore) { g_return_if_fail (DEX_IS_SEMAPHORE (semaphore)); dex_object_lock (semaphore); #ifdef HAVE_EVENTFD if (semaphore->eventfd != -1) { close (semaphore->eventfd); semaphore->eventfd = -1; } #endif if (semaphore->waiters.length > 0) { GQueue queue = semaphore->waiters; semaphore->waiters = (GQueue) {NULL, NULL, 0}; while (queue.length) { DexSemaphoreWaiter *waiter = g_queue_pop_head_link (&queue)->data; dex_future_complete (DEX_FUTURE (waiter), NULL, g_error_copy (&semaphore_closed_error)); dex_unref (waiter); } } dex_object_unlock (semaphore); } 0707010000005C000081A40000000000000000000000016712D33C00000ADA000000000000000000000000000000000000002500000000libdex-0.8.1/src/dex-stack-private.h/* dex-stack-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> G_BEGIN_DECLS typedef struct _DexStack DexStack; typedef struct _DexStackPool DexStackPool; struct _DexStack { GList link; gsize size; #ifdef G_OS_UNIX gpointer base; gpointer guard; gpointer ptr; #endif }; struct _DexStackPool { GMutex mutex; GQueue stacks; gsize stack_size; guint min_pool_size; guint max_pool_size; guint mark_unused : 1; }; DexStackPool *dex_stack_pool_new (gsize stack_size, int min_pool_size, int max_pool_size); void dex_stack_pool_free (DexStackPool *stack_pool); DexStack *dex_stack_new (gsize size); void dex_stack_free (DexStack *stack); void dex_stack_mark_unused (DexStack *stack); static inline DexStack * dex_stack_pool_acquire (DexStackPool *stack_pool) { DexStack *ret; g_assert (stack_pool != NULL); g_mutex_lock (&stack_pool->mutex); if (stack_pool->stacks.length > 0) { ret = g_queue_pop_head_link (&stack_pool->stacks)->data; g_mutex_unlock (&stack_pool->mutex); } else { g_mutex_unlock (&stack_pool->mutex); ret = dex_stack_new (stack_pool->stack_size); } return ret; } static inline void dex_stack_pool_release (DexStackPool *stack_pool, DexStack *stack) { g_assert (stack_pool != NULL); g_assert (stack->link.data == stack); g_assert (stack->link.prev == NULL); g_assert (stack->link.next == NULL); g_mutex_lock (&stack_pool->mutex); if (stack_pool->stacks.length > stack_pool->max_pool_size) { g_mutex_unlock (&stack_pool->mutex); dex_stack_free (stack); } else { if (stack_pool->mark_unused) dex_stack_mark_unused (stack); g_queue_push_head_link (&stack_pool->stacks, &stack->link); g_mutex_unlock (&stack_pool->mutex); } } G_END_DECLS 0707010000005D000081A40000000000000000000000016712D33C0000120E000000000000000000000000000000000000001D00000000libdex-0.8.1/src/dex-stack.c/* dex-stack.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <glib.h> #ifdef G_OS_UNIX # include <errno.h> # include <sys/mman.h> #endif #include "dex-platform.h" #include "dex-stack-private.h" #define DEFAULT_STACK_SIZE (MAX (4096*16, dex_get_min_stack_size())) #define DEFAULT_MIN_POOL_SIZE 4 #define DEFAULT_MAX_POOL_SIZE 16 DexStackPool * dex_stack_pool_new (gsize stack_size, int min_pool_size, int max_pool_size) { DexStackPool *stack_pool; if (stack_size == 0) stack_size = DEFAULT_STACK_SIZE; if (min_pool_size < 0) min_pool_size = DEFAULT_MIN_POOL_SIZE; if (max_pool_size < 0) max_pool_size = DEFAULT_MAX_POOL_SIZE; g_return_val_if_fail (min_pool_size <= max_pool_size, NULL); stack_pool = g_new0 (DexStackPool, 1); stack_pool->min_pool_size = min_pool_size; stack_pool->max_pool_size = max_pool_size; stack_pool->stack_size = stack_size; g_mutex_init (&stack_pool->mutex); for (guint i = 0; i < stack_pool->min_pool_size; i++) { DexStack *stack = dex_stack_new (stack_size); g_queue_push_head_link (&stack_pool->stacks, &stack->link); } return stack_pool; } void dex_stack_pool_free (DexStackPool *stack_pool) { g_return_if_fail (stack_pool != NULL); while (stack_pool->stacks.length > 0) { DexStack *stack = g_queue_pop_head_link (&stack_pool->stacks)->data; dex_stack_free (stack); } g_mutex_clear (&stack_pool->mutex); g_free (stack_pool); } DexStack * dex_stack_new (gsize size) { gsize page_size = dex_get_page_size (); DexStack *stack; #ifdef G_OS_UNIX gpointer map; gpointer guard; int flags = 0; #endif if (size < dex_get_min_stack_size ()) size = DEFAULT_STACK_SIZE; /* Round up to next full page size */ if ((size & (page_size-1)) != 0) size = (size + page_size) & ~(page_size-1); /* Make sure the stack size we're about to allocate is reasonable */ g_assert_cmpuint (size, >=, page_size); g_assert_cmpuint (size, <, G_MAXUINT32); #ifdef G_OS_UNIX flags = MAP_PRIVATE | MAP_ANONYMOUS; #if defined(__OpenBSD__) flags |= MAP_STACK; #endif /* mmap() the stack with an extra page for our guard page */ map = mmap (NULL, size + page_size, PROT_READ|PROT_WRITE, flags, -1, 0); if (MAP_FAILED == map) { int errsv = errno; g_error ("Failed to allocate stack: %s", g_strerror (errsv)); } #ifdef __IA64__ /* Itanium has a "register stack", see * itanium-software-runtime-architecture-guide.pdf for details. */ guard = (gpointer)((gintptr)map + ((size / 2) & ~page_size)); #elif G_HAVE_GROWING_STACK guard = (gpointer)((gintptr)map + size); #else guard = map; #endif /* Setup guard page to fault */ #if HAVE_MPROTECT if (mprotect (guard, page_size, PROT_NONE) != 0) { int errsv = errno; g_error ("Failed to protect stack guard page: %s", g_strerror (errsv)); } #endif #endif stack = g_new0 (DexStack, 1); stack->link.data = stack; stack->size = size; #ifdef G_OS_UNIX stack->base = map; stack->guard = guard; if (map == guard) stack->ptr = (gpointer)((gintptr)map + page_size); else stack->ptr = map; #endif return stack; } void dex_stack_free (DexStack *stack) { #ifdef G_OS_UNIX guint page_size = dex_get_page_size (); g_assert (stack->link.data == stack); g_assert (stack->link.prev == NULL); g_assert (stack->link.next == NULL); if (stack->base != MAP_FAILED) munmap (stack->base, stack->size + page_size); stack->base = MAP_FAILED; stack->guard = MAP_FAILED; stack->size = 0; stack->link.data = NULL; g_free (stack); #endif } void dex_stack_mark_unused (DexStack *stack) { g_assert (stack != NULL); g_assert (stack->link.data == stack); #ifdef HAVE_MADVISE madvise (stack->ptr, stack->size, MADV_DONTNEED); #endif } 0707010000005E000081A40000000000000000000000016712D33C000003F6000000000000000000000000000000000000002D00000000libdex-0.8.1/src/dex-static-future-private.h/* dex-static-future-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-static-future.h" G_BEGIN_DECLS DexFuture *dex_static_future_new_rejected (GError *error); DexFuture *dex_static_future_new_resolved (const GValue *value); G_END_DECLS 0707010000005F000081A40000000000000000000000016712D33C00000960000000000000000000000000000000000000002500000000libdex-0.8.1/src/dex-static-future.c/* dex-static-future.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-future-private.h" #include "dex-static-future-private.h" /** * DexStaticFuture: * * `DexStaticFuture` represents a future that is resolved from the initial * state. * * Use this when you need to create a future for API reasons but already have * the value or rejection at that point. * * #DexStaticFuture is used internally by functions like * dex_future_new_for_boolean() and similar. */ struct _DexStaticFuture { DexFuture parent_instance; }; typedef struct _DexStaticFutureClass { DexFutureClass parent_class; } DexStaticFutureClass; DEX_DEFINE_FINAL_TYPE (DexStaticFuture, dex_static_future, DEX_TYPE_FUTURE) #undef DEX_TYPE_STATIC_FUTURE #define DEX_TYPE_STATIC_FUTURE dex_static_future_type static void dex_static_future_class_init (DexStaticFutureClass *static_future_class) { } static void dex_static_future_init (DexStaticFuture *static_future) { } DexFuture * dex_static_future_new_rejected (GError *error) { DexFuture *ret; g_return_val_if_fail (error != NULL, NULL); ret = (DexFuture *)dex_object_create_instance (DEX_TYPE_STATIC_FUTURE); ret->rejected = error; ret->status = DEX_FUTURE_STATUS_REJECTED; return DEX_FUTURE (ret); } DexFuture * dex_static_future_new_resolved (const GValue *value) { DexFuture *ret; g_return_val_if_fail (G_IS_VALUE (value), NULL); ret = (DexFuture *)dex_object_create_instance (DEX_TYPE_STATIC_FUTURE); g_value_init (&ret->resolved, G_VALUE_TYPE (value)); g_value_copy (value, &ret->resolved); ret->status = DEX_FUTURE_STATUS_RESOLVED; return DEX_FUTURE (ret); } 07070100000060000081A40000000000000000000000016712D33C000004ED000000000000000000000000000000000000002500000000libdex-0.8.1/src/dex-static-future.h/* dex-static-future.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_STATIC_FUTURE (dex_static_future_get_type()) #define DEX_STATIC_FUTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_STATIC_FUTURE, DexStaticFuture)) #define DEX_IS_STATIC_FUTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_STATIC_FUTURE)) typedef struct _DexStaticFuture DexStaticFuture; DEX_AVAILABLE_IN_ALL GType dex_static_future_get_type (void) G_GNUC_CONST; G_END_DECLS 07070100000061000081A40000000000000000000000016712D33C0000209D000000000000000000000000000000000000002D00000000libdex-0.8.1/src/dex-thread-pool-scheduler.c/* * dex-thread-pool-scheduler.c * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-scheduler-private.h" #include "dex-thread-pool-scheduler.h" #include "dex-thread-pool-worker-private.h" #include "dex-thread-storage-private.h" #include "dex-work-queue-private.h" /** * DexThreadPoolScheduler: * * #DexThreadPoolScheduler is a #DexScheduler that will dispatch work items * and fibers to sub-schedulers on a specific operating system thread. * * #DexFiber will never migrate from the thread they are created on to reduce * chances of safety issues involved in tracking state between CPU. * * New work items are placed into a global work queue and then dispatched * efficiently to a single thread pool worker using a specialized async * semaphore. On modern Linux using io_uring, this wakes up a single worker * thread and therefore is not subject to "thundering herd" common with * global work queues. * * When a worker creates a new work item, it is placed into a work stealing * queue owned by the thread. Other worker threads may steal work items when * they have exhausted their own work queue. */ struct _DexThreadPoolScheduler { DexScheduler parent_instance; DexWorkQueue *global_work_queue; DexThreadPoolWorkerSet *set; GPtrArray *workers; guint fiber_rrobin; }; typedef struct _DexThreadPoolSchedulerClass { DexSchedulerClass parent_class; } DexThreadPoolSchedulerClass; DEX_DEFINE_FINAL_TYPE (DexThreadPoolScheduler, dex_thread_pool_scheduler, DEX_TYPE_SCHEDULER) #undef DEX_TYPE_THREAD_POOL_SCHEDULER #define DEX_TYPE_THREAD_POOL_SCHEDULER dex_thread_pool_scheduler_type static void dex_thread_pool_scheduler_push (DexScheduler *scheduler, DexWorkItem work_item) { DexThreadPoolScheduler *thread_pool_scheduler = DEX_THREAD_POOL_SCHEDULER (scheduler); DexThreadPoolWorker *worker = DEX_THREAD_POOL_WORKER_CURRENT; if (worker != NULL) DEX_SCHEDULER_GET_CLASS (worker)->push (DEX_SCHEDULER (worker), work_item); else dex_work_queue_push (thread_pool_scheduler->global_work_queue, work_item); } static GMainContext * dex_thread_pool_scheduler_get_main_context (DexScheduler *scheduler) { DexThreadPoolWorker *worker = DEX_THREAD_POOL_WORKER_CURRENT; /* Give the worker's main context if we're on a pooled thread */ if (worker != NULL) return dex_scheduler_get_main_context (DEX_SCHEDULER (worker)); /* Otherwise give the application default (main thread) context */ return dex_scheduler_get_main_context (dex_scheduler_get_default ()); } static DexAioContext * dex_thread_pool_scheduler_get_aio_context (DexScheduler *scheduler) { DexThreadPoolWorker *worker = DEX_THREAD_POOL_WORKER_CURRENT; /* Give the worker's aio context if we're on a pooled thread */ if (worker != NULL) return dex_scheduler_get_aio_context (DEX_SCHEDULER (worker)); /* Otherwise give the application default (main thread) aio context */ return dex_scheduler_get_aio_context (dex_scheduler_get_default ()); } static void dex_thread_pool_scheduler_spawn (DexScheduler *scheduler, DexFiber *fiber) { DexThreadPoolScheduler *thread_pool_scheduler = (DexThreadPoolScheduler *)scheduler; guint worker_index = g_atomic_int_add (&thread_pool_scheduler->fiber_rrobin, 1) % thread_pool_scheduler->workers->len; DexThreadPoolWorker *worker = thread_pool_scheduler->workers->pdata[worker_index]; /* TODO: This is just doing a dumb round robin for assigning a fiber to a * specific thread pool worker. We probably want something more interesting * than that so we can have weighted workers or even keep affinity to a small * number of them until latency reaches some threshold. */ DEX_SCHEDULER_GET_CLASS (worker)->spawn (DEX_SCHEDULER (worker), fiber); } static void dex_thread_pool_scheduler_finalize (DexObject *object) { DexThreadPoolScheduler *thread_pool_scheduler = (DexThreadPoolScheduler *)object; dex_clear (&thread_pool_scheduler->global_work_queue); g_clear_pointer (&thread_pool_scheduler->set, dex_thread_pool_worker_set_unref); g_clear_pointer (&thread_pool_scheduler->workers, g_ptr_array_unref); DEX_OBJECT_CLASS (dex_thread_pool_scheduler_parent_class)->finalize (object); } static void dex_thread_pool_scheduler_class_init (DexThreadPoolSchedulerClass *thread_pool_scheduler_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (thread_pool_scheduler_class); DexSchedulerClass *scheduler_class = DEX_SCHEDULER_CLASS (thread_pool_scheduler_class); object_class->finalize = dex_thread_pool_scheduler_finalize; scheduler_class->get_main_context = dex_thread_pool_scheduler_get_main_context; scheduler_class->get_aio_context = dex_thread_pool_scheduler_get_aio_context; scheduler_class->push = dex_thread_pool_scheduler_push; scheduler_class->spawn = dex_thread_pool_scheduler_spawn; } static void dex_thread_pool_scheduler_init (DexThreadPoolScheduler *thread_pool_scheduler) { thread_pool_scheduler->global_work_queue = dex_work_queue_new (); thread_pool_scheduler->set = dex_thread_pool_worker_set_new (); thread_pool_scheduler->workers = g_ptr_array_new_with_free_func (dex_unref); } /** * dex_thread_pool_scheduler_new: * * Creates a new #DexScheduler that executes work items on a thread pool. * * Returns: (transfer full): a #DexThreadPoolScheduler */ DexScheduler * dex_thread_pool_scheduler_new (void) { DexThreadPoolScheduler *thread_pool_scheduler; guint n_procs; guint n_workers; thread_pool_scheduler = (DexThreadPoolScheduler *)dex_object_create_instance (DEX_TYPE_THREAD_POOL_SCHEDULER); /* TODO: let this be dynamic and tunable, as well as thread pinning */ n_procs = MIN (32, g_get_num_processors ()); /* Couple things here, which we should take a look at in the future to * see how we can tune them correctly, but: * * Remove one as the main thread has an AIO context too and we don't want * to create contention there. Also, io_uring may limit us in the number * of io_uring we can create. * * g_get_num_processors() includes hyperthreads, so take the result and * cut it in half. It would be nicer to actually verify this on the system * for cases where we don't have that. * * Additionally, bail if the worker fails to be created. */ n_workers = MAX (1, (n_procs/2)); for (guint i = 0; i < n_workers; i++) { DexThreadPoolWorker *thread_pool_worker; thread_pool_worker = dex_thread_pool_worker_new (thread_pool_scheduler->global_work_queue, thread_pool_scheduler->set); if (thread_pool_worker != NULL) g_ptr_array_add (thread_pool_scheduler->workers, g_steal_pointer (&thread_pool_worker)); else break; } return DEX_SCHEDULER (thread_pool_scheduler); } /** * dex_thread_pool_scheduler_get_default: * * Gets the default thread pool scheduler for the instance. * * This function is useful to allow programs and libraries to share * an off-main-thread scheduler without having to coordinate on where * the scheduler instance is created or owned. * * Returns: (transfer none): a #DexScheduler */ DexScheduler * dex_thread_pool_scheduler_get_default (void) { static DexScheduler *default_thread_pool; if (g_once_init_enter (&default_thread_pool)) { DexScheduler *instance = dex_thread_pool_scheduler_new (); g_once_init_leave (&default_thread_pool, instance); } return default_thread_pool; } 07070100000062000081A40000000000000000000000016712D33C0000064B000000000000000000000000000000000000002D00000000libdex-0.8.1/src/dex-thread-pool-scheduler.h/* * dex-thread-pool-scheduler.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-scheduler.h" G_BEGIN_DECLS #define DEX_TYPE_THREAD_POOL_SCHEDULER (dex_thread_pool_scheduler_get_type()) #define DEX_THREAD_POOL_SCHEDULER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_THREAD_POOL_SCHEDULER, DexThreadPoolScheduler)) #define DEX_IS_THREAD_POOL_SCHEDULER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_THREAD_POOL_SCHEDULER)) typedef struct _DexThreadPoolScheduler DexThreadPoolScheduler; DEX_AVAILABLE_IN_ALL GType dex_thread_pool_scheduler_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexScheduler *dex_thread_pool_scheduler_new (void); DEX_AVAILABLE_IN_ALL DexScheduler *dex_thread_pool_scheduler_get_default (void) G_GNUC_CONST; G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexThreadPoolScheduler, dex_unref) G_END_DECLS 07070100000063000081A40000000000000000000000016712D33C00000766000000000000000000000000000000000000003200000000libdex-0.8.1/src/dex-thread-pool-worker-private.h/* * dex-thread-pool-worker-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-object-private.h" #include "dex-scheduler-private.h" #include "dex-work-queue-private.h" G_BEGIN_DECLS #define DEX_TYPE_THREAD_POOL_WORKER (dex_thread_pool_worker_get_type()) #define DEX_THREAD_POOL_WORKER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_THREAD_POOL_WORKER, DexThreadPoolWorker)) #define DEX_IS_THREAD_POOL_WORKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_THREAD_POOL_WORKER)) typedef struct _DexThreadPoolWorker DexThreadPoolWorker; typedef struct _DexThreadPoolWorkerSet DexThreadPoolWorkerSet; GType dex_thread_pool_worker_get_type (void) G_GNUC_CONST; DexThreadPoolWorker *dex_thread_pool_worker_new (DexWorkQueue *work_queue, DexThreadPoolWorkerSet *set); DexThreadPoolWorkerSet *dex_thread_pool_worker_set_new (void); DexThreadPoolWorkerSet *dex_thread_pool_worker_set_ref (DexThreadPoolWorkerSet *set); void dex_thread_pool_worker_set_unref (DexThreadPoolWorkerSet *set); G_END_DECLS 07070100000064000081A40000000000000000000000016712D33C00004A8C000000000000000000000000000000000000002A00000000libdex-0.8.1/src/dex-thread-pool-worker.c/* * dex-thread-pool-worker.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <stdatomic.h> #include "dex-aio-backend-private.h" #include "dex-compat-private.h" #include "dex-fiber-private.h" #include "dex-thread-pool-worker-private.h" #include "dex-thread-storage-private.h" #include "dex-work-stealing-queue-private.h" #include "dex-work-queue-private.h" typedef enum _DexThreadPoolWorkerStatus { DEX_THREAD_POOL_WORKER_INITIAL, DEX_THREAD_POOL_WORKER_RUNNING, DEX_THREAD_POOL_WORKER_STOPPING, DEX_THREAD_POOL_WORKER_FINISHED, } DexThreadPoolWorkerStatus; struct _DexThreadPoolWorker { DexScheduler parent_instance; GList set_link; DexThreadPoolWorkerSet *set; GThread *thread; GMainContext *main_context; GMainLoop *main_loop; DexAioContext *aio_context; DexWorkQueue *global_work_queue; DexWorkStealingQueue *work_stealing_queue; GSource *set_source; GSource *local_source; GSource *fiber_scheduler; GMutex setup_mutex; GCond setup_cond; DexThreadPoolWorkerStatus status : 2; }; typedef struct _DexThreadPoolWorkerClass { DexSchedulerClass parent_class; } DexThreadPoolWorkerClass; DEX_DEFINE_FINAL_TYPE (DexThreadPoolWorker, dex_thread_pool_worker, DEX_TYPE_SCHEDULER) #undef DEX_TYPE_THREAD_POOL_WORKER #define DEX_TYPE_THREAD_POOL_WORKER dex_thread_pool_worker_type static void dex_thread_pool_worker_set_add (DexThreadPoolWorkerSet *set, DexThreadPoolWorker *thread_pool_worker); static void dex_thread_pool_worker_set_remove (DexThreadPoolWorkerSet *set, DexThreadPoolWorker *thread_pool_worker); static GSource *dex_thread_pool_worker_set_create_source (DexThreadPoolWorkerSet *set, DexThreadPoolWorker *thread_pool_worker); static gboolean dex_thread_pool_worker_work_item_cb (gpointer user_data) { DexWorkItem *work_item = user_data; dex_work_item_invoke (work_item); return G_SOURCE_REMOVE; } static void dex_thread_pool_worker_push (DexScheduler *scheduler, DexWorkItem work_item) { DexThreadPoolWorker *thread_pool_worker = DEX_THREAD_POOL_WORKER (scheduler); g_assert (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker)); if G_LIKELY (g_thread_self () == thread_pool_worker->thread && thread_pool_worker->status == DEX_THREAD_POOL_WORKER_RUNNING) dex_work_stealing_queue_push (thread_pool_worker->work_stealing_queue, work_item); else { GSource *source; /* Pushing a work item directly onto a worker is generally going * to be related to completing work items. Treat those as extremely * high priority as they will delay further processing of futures. * * This is currently a workaround to improve the situation with * issue #17 when we're on fallback configurations. If we want * to improve that situation further, we can reduce some overhead * here by creating a dedicated GSource like DexMainScheduler does * for work items instead of a 1:1 GSource creation. * * But this at least improves the situation immediately that is * causing potential lock-ups. */ source = g_idle_source_new (); g_source_set_priority (source, G_MININT); g_source_set_callback (source, dex_thread_pool_worker_work_item_cb, g_memdup2 (&work_item, sizeof work_item), g_free); g_source_attach (source, thread_pool_worker->main_context); g_source_unref (source); } } static gboolean dex_thread_pool_worker_finalize_cb (gpointer data) { DexThreadPoolWorker *thread_pool_worker = DEX_THREAD_POOL_WORKER (data); DexWorkItem work_item; /* First set the status to ensure that our check/dispatch will bail */ thread_pool_worker->status = DEX_THREAD_POOL_WORKER_STOPPING; g_main_loop_quit (thread_pool_worker->main_loop); /* Now flush out the rest of the work items if there are any */ while (dex_work_stealing_queue_pop (thread_pool_worker->work_stealing_queue, &work_item)) dex_work_item_invoke (&work_item); return G_SOURCE_REMOVE; } static void dex_thread_pool_worker_finalize (DexObject *object) { DexThreadPoolWorker *thread_pool_worker = DEX_THREAD_POOL_WORKER (object); GSource *idle_source; g_assert (thread_pool_worker->thread != g_thread_self ()); /* To finalize the worker, we need to push a work item to the thread * that will cause it to stop processing and then wait for the thread * to exit after processing the rest of the queue. We change the finalizing * bit on the worker thread so the common case (it's FALSE) doesn't require * any sort of synchronization/atomic operations every loop. */ idle_source = g_idle_source_new (); _g_source_set_static_name (idle_source, "[dex-thread-pool-worker-finalize]"); g_source_set_priority (idle_source, G_MININT); g_source_set_callback (idle_source, dex_thread_pool_worker_finalize_cb, thread_pool_worker, NULL); g_source_attach (idle_source, thread_pool_worker->main_context); g_source_unref (idle_source); /* Now wait for the thread to process items and exit the thread */ g_thread_join (thread_pool_worker->thread); #ifdef G_ENABLE_DEBUG atomic_thread_fence (memory_order_seq_cst); g_assert (thread_pool_worker->status == DEX_THREAD_POOL_WORKER_FINISHED); g_assert (dex_work_stealing_queue_empty (thread_pool_worker->queue)); #endif /* These are all destroyed during thread shutdown */ g_clear_pointer (&thread_pool_worker->set_source, g_source_unref); g_clear_pointer (&thread_pool_worker->local_source, g_source_unref); g_clear_pointer (&thread_pool_worker->fiber_scheduler, g_source_unref); g_clear_pointer (&thread_pool_worker->thread, g_thread_unref); g_clear_pointer (&thread_pool_worker->main_context, g_main_context_unref); g_clear_pointer (&thread_pool_worker->main_loop, g_main_loop_unref); g_clear_pointer (&thread_pool_worker->work_stealing_queue, dex_work_stealing_queue_unref); dex_clear (&thread_pool_worker->global_work_queue); g_assert (thread_pool_worker->set_link.prev == NULL); g_assert (thread_pool_worker->set_link.next == NULL); g_mutex_clear (&thread_pool_worker->setup_mutex); g_cond_clear (&thread_pool_worker->setup_cond); DEX_OBJECT_CLASS (dex_thread_pool_worker_parent_class)->finalize (object); } static GMainContext * dex_thread_pool_worker_get_main_context (DexScheduler *scheduler) { DexThreadPoolWorker *thread_pool_worker = DEX_THREAD_POOL_WORKER (scheduler); g_assert (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker)); return thread_pool_worker->main_context; } static DexAioContext * dex_thread_pool_worker_get_aio_context (DexScheduler *scheduler) { DexThreadPoolWorker *thread_pool_worker = DEX_THREAD_POOL_WORKER (scheduler); g_assert (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker)); return thread_pool_worker->aio_context; } static void dex_thread_pool_worker_spawn (DexScheduler *scheduler, DexFiber *fiber) { DexThreadPoolWorker *thread_pool_worker = DEX_THREAD_POOL_WORKER (scheduler); g_assert (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker)); dex_fiber_scheduler_register ((DexFiberScheduler *)thread_pool_worker->fiber_scheduler, fiber); } static void dex_thread_pool_worker_class_init (DexThreadPoolWorkerClass *thread_pool_worker_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (thread_pool_worker_class); DexSchedulerClass *scheduler_class = DEX_SCHEDULER_CLASS (thread_pool_worker_class); object_class->finalize = dex_thread_pool_worker_finalize; scheduler_class->push = dex_thread_pool_worker_push; scheduler_class->get_main_context = dex_thread_pool_worker_get_main_context; scheduler_class->spawn = dex_thread_pool_worker_spawn; scheduler_class->get_aio_context = dex_thread_pool_worker_get_aio_context; } static void dex_thread_pool_worker_init (DexThreadPoolWorker *thread_pool_worker) { thread_pool_worker->set_link.data = thread_pool_worker; g_mutex_init (&thread_pool_worker->setup_mutex); g_cond_init (&thread_pool_worker->setup_cond); } static gpointer dex_thread_pool_worker_thread_func (gpointer data) { DexThreadPoolWorker *thread_pool_worker = DEX_THREAD_POOL_WORKER (data); DexThreadStorage *storage = dex_thread_storage_get (); DexAioBackend *aio_backend; DexAioContext *aio_context; DexFuture *global_work_queue_loop; GSource *source; g_mutex_lock (&thread_pool_worker->setup_mutex); aio_backend = dex_aio_backend_get_default (); aio_context = dex_aio_backend_create_context (aio_backend); /* If we fail to setup an AIO context, then there is no point in * adding this thread pool worker. Just bail immediately and notify * the creator of the issue. */ if (aio_context == NULL) { thread_pool_worker->status = DEX_THREAD_POOL_WORKER_FINISHED; g_cond_signal (&thread_pool_worker->setup_cond); g_mutex_unlock (&thread_pool_worker->setup_mutex); return NULL; } thread_pool_worker->aio_context = g_steal_pointer (&aio_context); /* Attach our AIO source to complete AIO work */ g_source_attach ((GSource *)thread_pool_worker->aio_context, thread_pool_worker->main_context); /* Attach a GSource that will process items from the worker threads * work queue. */ source = dex_work_stealing_queue_create_source (thread_pool_worker->work_stealing_queue); g_source_set_priority (source, G_PRIORITY_DEFAULT); g_source_attach (source, thread_pool_worker->main_context); thread_pool_worker->local_source = g_steal_pointer (&source); /* Create a source to steal work items from other thread pool threads. * This is slightly higher priority than the global queue because we * want to steal items from the peers before the global queue. */ source = dex_thread_pool_worker_set_create_source (thread_pool_worker->set, thread_pool_worker); g_source_set_priority (source, G_PRIORITY_DEFAULT_IDLE-1); g_source_attach (source, thread_pool_worker->main_context); thread_pool_worker->set_source = g_steal_pointer (&source); /* Setup fiber scheduler source */ source = (GSource *)dex_fiber_scheduler_new (); g_source_attach (source, thread_pool_worker->main_context); thread_pool_worker->fiber_scheduler = g_steal_pointer (&source); storage->scheduler = DEX_SCHEDULER (thread_pool_worker); storage->worker = thread_pool_worker; storage->aio_context = thread_pool_worker->aio_context; g_main_context_push_thread_default (thread_pool_worker->main_context); thread_pool_worker->status = DEX_THREAD_POOL_WORKER_RUNNING; /* Add to set so others may steal work items from us */ dex_thread_pool_worker_set_add (thread_pool_worker->set, thread_pool_worker); /* Async processing global work-queue items until we're told to shutdown */ global_work_queue_loop = dex_work_queue_run (thread_pool_worker->global_work_queue); /* Notify the caller that we are all setup */ g_cond_signal (&thread_pool_worker->setup_cond); g_mutex_unlock (&thread_pool_worker->setup_mutex); /* Process main context until we are told to shutdown */ g_main_loop_run (thread_pool_worker->main_loop); /* Discard our work queue loop */ dex_clear (&global_work_queue_loop); /* Flush out any pending operations */ while (g_main_context_pending (thread_pool_worker->main_context)) g_main_context_iteration (thread_pool_worker->main_context, FALSE); /* Remove from set now so others will not try to steal from us */ dex_thread_pool_worker_set_remove (thread_pool_worker->set, thread_pool_worker); /* Ensure our sources will not continue on */ g_source_destroy (thread_pool_worker->set_source); g_source_destroy (thread_pool_worker->local_source); g_source_destroy (thread_pool_worker->fiber_scheduler); thread_pool_worker->status = DEX_THREAD_POOL_WORKER_FINISHED; g_main_context_pop_thread_default (thread_pool_worker->main_context); storage->worker = NULL; storage->scheduler = NULL; storage->aio_context = NULL; return NULL; } static gboolean dex_thread_pool_worker_maybe_steal (DexThreadPoolWorker *thread_pool_worker, DexThreadPoolWorker *neighbor) { DexWorkItem work_item; g_assert (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker)); g_assert (DEX_IS_THREAD_POOL_WORKER (neighbor)); if (dex_work_stealing_queue_steal (neighbor->work_stealing_queue, &work_item)) { dex_work_item_invoke (&work_item); return TRUE; } return FALSE; } typedef struct _DexThreadPoolWorkerSet { GQueue queue; GRWLock rwlock; } DexThreadPoolWorkerSet; DexThreadPoolWorkerSet * dex_thread_pool_worker_set_new (void) { DexThreadPoolWorkerSet *set; set = g_atomic_rc_box_new0 (DexThreadPoolWorkerSet); g_rw_lock_init (&set->rwlock); return set; } static void dex_thread_pool_worker_set_add (DexThreadPoolWorkerSet *set, DexThreadPoolWorker *thread_pool_worker) { g_return_if_fail (set != NULL); g_return_if_fail (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker)); g_return_if_fail (thread_pool_worker->set_link.prev == NULL); g_return_if_fail (thread_pool_worker->set_link.next == NULL); g_rw_lock_writer_lock (&set->rwlock); g_queue_push_tail_link (&set->queue, &thread_pool_worker->set_link); g_rw_lock_writer_unlock (&set->rwlock); } static void dex_thread_pool_worker_set_remove (DexThreadPoolWorkerSet *set, DexThreadPoolWorker *thread_pool_worker) { g_return_if_fail (set != NULL); g_return_if_fail (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker)); g_rw_lock_writer_lock (&set->rwlock); g_queue_unlink (&set->queue, &thread_pool_worker->set_link); g_rw_lock_writer_unlock (&set->rwlock); } DexThreadPoolWorkerSet * dex_thread_pool_worker_set_ref (DexThreadPoolWorkerSet *set) { g_atomic_rc_box_acquire (set); return set; } static void dex_thread_pool_worker_set_finalize (gpointer data) { DexThreadPoolWorkerSet *set = data; while (set->queue.length > 0) dex_thread_pool_worker_set_remove (set, g_queue_peek_head (&set->queue)); g_rw_lock_clear (&set->rwlock); } void dex_thread_pool_worker_set_unref (DexThreadPoolWorkerSet *set) { g_atomic_rc_box_release_full (set, dex_thread_pool_worker_set_finalize); } static inline void dex_thread_pool_worker_set_foreach (DexThreadPoolWorkerSet *set, DexThreadPoolWorker *head) { g_rw_lock_reader_lock (&set->rwlock); for (const GList *iter = head->set_link.next; iter; iter = iter->next) { if (dex_thread_pool_worker_maybe_steal (head, iter->data)) goto unlock; } for (const GList *iter = set->queue.head; iter->data != head; iter = iter->next) { if (dex_thread_pool_worker_maybe_steal (head, iter->data)) goto unlock; } unlock: g_rw_lock_reader_unlock (&set->rwlock); } typedef struct _DexThreadPoolWorkerSetSource { GSource parent_source; DexThreadPoolWorkerSet *set; DexThreadPoolWorker *thread_pool_worker; } DexThreadPoolWorkerSetSource; static gboolean dex_thread_pool_worker_set_source_check (GSource *source) { /* We always check the peers for work */ return TRUE; } static gboolean dex_thread_pool_worker_set_source_dispatch (GSource *source, GSourceFunc callback, gpointer callback_data) { DexThreadPoolWorkerSetSource *real_source = (DexThreadPoolWorkerSetSource *)source; dex_thread_pool_worker_set_foreach (real_source->set, real_source->thread_pool_worker); return G_SOURCE_CONTINUE; } static GSourceFuncs dex_thread_pool_worker_set_source_funcs = { .check = dex_thread_pool_worker_set_source_check, .dispatch = dex_thread_pool_worker_set_source_dispatch, }; static GSource * dex_thread_pool_worker_set_create_source (DexThreadPoolWorkerSet *set, DexThreadPoolWorker *thread_pool_worker) { DexThreadPoolWorkerSetSource *source; g_return_val_if_fail (set != NULL, NULL); g_return_val_if_fail (DEX_IS_THREAD_POOL_WORKER (thread_pool_worker), NULL); source = (DexThreadPoolWorkerSetSource *) g_source_new (&dex_thread_pool_worker_set_source_funcs, sizeof *source); _g_source_set_static_name ((GSource *)source, "[dex-thread-pool-worker-set]"); source->set = set; source->thread_pool_worker = thread_pool_worker; return (GSource *)source; } DexThreadPoolWorker * dex_thread_pool_worker_new (DexWorkQueue *work_queue, DexThreadPoolWorkerSet *set) { DexThreadPoolWorker *thread_pool_worker; gboolean failed; g_return_val_if_fail (work_queue != NULL, NULL); g_return_val_if_fail (set != NULL, NULL); thread_pool_worker = (DexThreadPoolWorker *)dex_object_create_instance (DEX_TYPE_THREAD_POOL_WORKER); thread_pool_worker->main_context = g_main_context_new (); thread_pool_worker->main_loop = g_main_loop_new (thread_pool_worker->main_context, FALSE); thread_pool_worker->global_work_queue = dex_ref (work_queue); thread_pool_worker->work_stealing_queue = dex_work_stealing_queue_new (255); thread_pool_worker->set = set; /* Now spawn our thread to process events via GSource */ g_mutex_lock (&thread_pool_worker->setup_mutex); thread_pool_worker->thread = g_thread_new ("dex-thread-pool-worker", dex_thread_pool_worker_thread_func, thread_pool_worker); g_cond_wait (&thread_pool_worker->setup_cond, &thread_pool_worker->setup_mutex); failed = thread_pool_worker->status == DEX_THREAD_POOL_WORKER_FINISHED; g_mutex_unlock (&thread_pool_worker->setup_mutex); if (failed) dex_clear (&thread_pool_worker); return thread_pool_worker; } 07070100000065000081A40000000000000000000000016712D33C000005C8000000000000000000000000000000000000002E00000000libdex-0.8.1/src/dex-thread-storage-private.h/* * dex-thread-storage-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> G_BEGIN_DECLS #define DEX_THREAD_POOL_WORKER_CURRENT (dex_thread_storage_get()->worker) #define DEX_DISPATCH_RECURSE_MAX 4 typedef struct _DexAioContext DexAioContext; typedef struct _DexFiberScheduler DexFiberScheduler; typedef struct _DexScheduler DexScheduler; typedef struct _DexThreadPoolWorker DexThreadPoolWorker; typedef struct _DexThreadStorage { DexScheduler *scheduler; DexThreadPoolWorker *worker; DexAioContext *aio_context; DexFiberScheduler *fiber_scheduler; guint sync_dispatch_depth; } DexThreadStorage; DexThreadStorage *dex_thread_storage_get (void); G_END_DECLS 07070100000066000081A40000000000000000000000016712D33C000004B7000000000000000000000000000000000000002600000000libdex-0.8.1/src/dex-thread-storage.c/* * dex-thread-storage.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-thread-storage-private.h" static GPrivate dex_thread_storage_key = G_PRIVATE_INIT (g_free); DexThreadStorage * dex_thread_storage_get (void) { DexThreadStorage *ret = g_private_get (&dex_thread_storage_key); if G_UNLIKELY (ret == NULL) { ret = g_new0 (DexThreadStorage, 1); g_private_set (&dex_thread_storage_key, ret); } return ret; } 07070100000067000081A40000000000000000000000016712D33C00001590000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-timeout.c/* * dex-timeout.c * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gio/gio.h> #include "dex-compat-private.h" #include "dex-error.h" #include "dex-future-private.h" #include "dex-scheduler.h" #include "dex-timeout.h" /** * DexTimeout: * * #DexTimeout is a #DexFuture that will resolve after the configured * period of time. */ typedef struct _DexTimeout { DexFuture parent_instance; GSource *source; } DexTimeout; typedef struct _DexTimeoutClass { DexFutureClass parent_class; } DexTimeoutClass; DEX_DEFINE_FINAL_TYPE (DexTimeout, dex_timeout, DEX_TYPE_FUTURE) static void dex_timeout_discard (DexFuture *future) { DexTimeout *timeout = DEX_TIMEOUT (future); if (timeout->source != NULL) g_source_destroy (timeout->source); } static void dex_timeout_finalize (DexObject *object) { DexTimeout *timeout = DEX_TIMEOUT (object); if (timeout->source != NULL) { if (!g_source_is_destroyed (timeout->source)) { g_critical ("%s destroyed while timer was active. " "This is likely a bug as no future is holding a reference to %p", DEX_OBJECT_TYPE_NAME (object), object); g_source_destroy (timeout->source); } g_clear_pointer (&timeout->source, g_source_unref); } DEX_OBJECT_CLASS (dex_timeout_parent_class)->finalize (object); } static void dex_timeout_class_init (DexTimeoutClass *timeout_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (timeout_class); DexFutureClass *future_class = DEX_FUTURE_CLASS (timeout_class); object_class->finalize = dex_timeout_finalize; future_class->discard = dex_timeout_discard; } static void dex_timeout_init (DexTimeout *timeout) { } static void clear_weak_ref (gpointer data) { dex_weak_ref_clear (data); g_free (data); } static gboolean dex_timeout_source_func (gpointer data) { DexWeakRef *wr = data; DexTimeout *timeout = dex_weak_ref_get (wr); g_assert (!timeout || DEX_IS_TIMEOUT (timeout)); if (timeout != NULL) { dex_future_complete (DEX_FUTURE (timeout), NULL, g_error_new_literal (DEX_ERROR, DEX_ERROR_TIMED_OUT, "Operation timed out")); dex_object_lock (timeout); g_clear_pointer (&timeout->source, g_source_unref); dex_object_unlock (timeout); dex_unref (timeout); } return G_SOURCE_REMOVE; } DexFuture * dex_timeout_new_deadline (gint64 deadline) { static const char *name; DexScheduler *scheduler; DexTimeout *timeout; DexWeakRef *wr; if G_UNLIKELY (name == NULL) name = g_intern_static_string ("[dex-timeout]"); timeout = (DexTimeout *)dex_object_create_instance (DEX_TYPE_TIMEOUT); wr = g_new0 (DexWeakRef, 1); dex_weak_ref_init (wr, timeout); timeout->source = g_timeout_source_new (0); g_source_set_ready_time (timeout->source, deadline); _g_source_set_static_name (timeout->source, name); g_source_set_priority (timeout->source, G_PRIORITY_DEFAULT); g_source_set_callback (timeout->source, dex_timeout_source_func, wr, clear_weak_ref); if (!(scheduler = dex_scheduler_get_thread_default ())) scheduler = dex_scheduler_get_default (); /* TODO: Delay attaching until timeout is awaited. * * Currently, this attaches the GSource when the timeout is created. This * can be the wrong thing to do when you are creating a bunch of futures and * then want them to run on a specific scheduler. * * In that case, you probably want to delay arming the timeout until something * on the thread has "awaited" it. * * Currently we don't have explicit awaiting though, so this will need to be * implemented first before we can do the above work. */ g_source_attach (timeout->source, dex_scheduler_get_main_context (scheduler)); return DEX_FUTURE (timeout); } DexFuture * dex_timeout_new_seconds (int seconds) { gint64 usec = (gint64) G_USEC_PER_SEC * seconds; return dex_timeout_new_deadline (g_get_monotonic_time () + usec); } DexFuture * dex_timeout_new_msec (int msec) { gint64 usec = (G_USEC_PER_SEC/1000L) * msec; return dex_timeout_new_deadline (g_get_monotonic_time () + usec); } DexFuture * dex_timeout_new_usec (gint64 usec) { return dex_timeout_new_deadline (g_get_monotonic_time () + usec); } void dex_timeout_postpone_until (DexTimeout *timeout, gint64 deadline) { g_return_if_fail (DEX_IS_TIMEOUT (timeout)); dex_object_lock (timeout); if (timeout->source != NULL) g_source_set_ready_time (timeout->source, deadline); dex_object_unlock (timeout); } 07070100000068000081A40000000000000000000000016712D33C0000073F000000000000000000000000000000000000001F00000000libdex-0.8.1/src/dex-timeout.h/* * dex-timeout.h * * Copyright 2022 Christian Hergert <chergert@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined (DEX_INSIDE) && !defined (DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_TIMEOUT (dex_timeout_get_type()) #define DEX_TIMEOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_TIMEOUT, DexTimeout)) #define DEX_IS_TIMEOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_TIMEOUT)) typedef struct _DexTimeout DexTimeout; DEX_AVAILABLE_IN_ALL GType dex_timeout_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexFuture *dex_timeout_new_deadline (gint64 deadline); DEX_AVAILABLE_IN_ALL DexFuture *dex_timeout_new_seconds (int seconds); DEX_AVAILABLE_IN_ALL DexFuture *dex_timeout_new_msec (int msec); DEX_AVAILABLE_IN_ALL DexFuture *dex_timeout_new_usec (gint64 usec); DEX_AVAILABLE_IN_ALL void dex_timeout_postpone_until (DexTimeout *timeout, gint64 deadline); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexTimeout, dex_unref) G_END_DECLS 07070100000069000081A40000000000000000000000016712D33C00000AB1000000000000000000000000000000000000002800000000libdex-0.8.1/src/dex-ucontext-private.h/* dex-ucontext-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <signal.h> #include <stdarg.h> #include <string.h> #include <unistd.h> #include "config.h" /* The following code is from libtask by Russ Cox to emulate the * ucontext implementation in a fashion that doesn't necessarily * require context switches for implementation. */ /* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ #if defined(__sun__) # define __EXTENSIONS__ 1 /* SunOS */ # if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__) /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */ # else # define __MAKECONTEXT_V2_SOURCE 1 # endif #endif #define USE_UCONTEXT 1 #if defined(__OpenBSD__) || defined(__mips__) # undef USE_UCONTEXT # define USE_UCONTEXT 0 #endif #if defined(__APPLE__) # ifndef _XOPEN_SOURCE # define _XOPEN_SOURCE # endif #endif #if USE_UCONTEXT # include <ucontext.h> #endif #if defined(__FreeBSD__) && __FreeBSD__ < 5 extern int getmcontext(mcontext_t*); extern void setmcontext(const mcontext_t*); # define setcontext(u) setmcontext(&(u)->uc_mcontext) # define getcontext(u) getmcontext(&(u)->uc_mcontext) extern int swapcontext(ucontext_t*, const ucontext_t*); extern void makecontext(ucontext_t*, void(*)(void), int, ...); #endif #if defined(__OpenBSD__) # define mcontext libthread_mcontext # define mcontext_t libthread_mcontext_t # define ucontext libthread_ucontext # define ucontext_t libthread_ucontext_t # if defined __i386__ # include "386-ucontext.h" # else # include "power-ucontext.h" # endif #endif #if defined(__linux__) && defined(__mips__) # if !defined(HAVE_UCONTEXT_H) # include "mips-ucontext.h" # endif extern int getcontext (ucontext_t *ucp); extern int setcontext(const ucontext_t *ucp); extern int swapcontext(ucontext_t *oucp, const ucontext_t *ucp); /* glibc makecontext.S for mips specifies int return type, not void */ extern void makecontext(ucontext_t *ucp, void(*)(void), int, ...); #endif 0707010000006A000081A40000000000000000000000016712D33C000007C4000000000000000000000000000000000000002000000000libdex-0.8.1/src/dex-ucontext.c/* The following code originals in libtask from Rus Cox. The original * license is provided below, and is the standard MIT license. */ /* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ #include "dex-ucontext-private.h" #if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5 # define NEEDX86MAKECONTEXT # define NEEDSWAPCONTEXT #endif #if defined(__OpenBSD__) && defined(__i386__) # define NEEDX86MAKECONTEXT # define NEEDSWAPCONTEXT #endif #ifdef NEEDPOWERMAKECONTEXT void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) { ulong *sp, *tos; va_list arg; tos = (ulong*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong); sp = tos - 16; ucp->mc.pc = (long)func; ucp->mc.sp = (long)sp; va_start(arg, argc); ucp->mc.r3 = va_arg(arg, long); va_end(arg); } #endif #ifdef NEEDX86MAKECONTEXT void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) { int *sp; sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4; sp -= argc; sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ memmove(sp, &argc+1, argc*sizeof(int)); *--sp = 0; /* return address */ ucp->uc_mcontext.mc_eip = (long)func; ucp->uc_mcontext.mc_esp = (int)sp; } #endif #ifdef NEEDAMD64MAKECONTEXT void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) { long *sp; va_list va; memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext); if(argc != 2) __builtin_trap (); va_start(va, argc); ucp->uc_mcontext.mc_rdi = va_arg(va, int); ucp->uc_mcontext.mc_rsi = va_arg(va, int); va_end(va); sp = (long*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(long); sp -= argc; sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ *--sp = 0; /* return address */ ucp->uc_mcontext.mc_rip = (long)func; ucp->uc_mcontext.mc_rsp = (long)sp; } #endif #ifdef NEEDSWAPCONTEXT int swapcontext(ucontext_t *oucp, const ucontext_t *ucp) { if(getcontext(oucp) == 0) setcontext(ucp); return 0; } #endif 0707010000006B000081A40000000000000000000000016712D33C0000110B000000000000000000000000000000000000002300000000libdex-0.8.1/src/dex-unix-signal.c/* * dex-unix-signal.c * * Copyright 2022-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <glib-unix.h> #include "dex-compat-private.h" #include "dex-future-private.h" #include "dex-unix-signal.h" /** * DexUnixSignal: * * #DexUnixSignal is a #DexFuture that will resolve when a specific unix * signal has been received. * * Use this when you want to handle a signal from your main loop rather than * from a resticted operating signal handler. * * On Linux, this uses a signalfd. */ struct _DexUnixSignal { DexFuture parent_instance; GSource *source; int signum; }; typedef struct _DexUnixSignalClass { DexFutureClass parent_class; } DexUnixSignalClass; DEX_DEFINE_FINAL_TYPE (DexUnixSignal, dex_unix_signal, DEX_TYPE_FUTURE) static void dex_unix_signal_finalize (DexObject *object) { DexUnixSignal *unix_signal = DEX_UNIX_SIGNAL (object); if (unix_signal->source != NULL) { g_source_destroy (unix_signal->source); g_clear_pointer (&unix_signal->source, g_source_unref); } DEX_OBJECT_CLASS (dex_unix_signal_parent_class)->finalize (object); } static void dex_unix_signal_class_init (DexUnixSignalClass *unix_signal_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (unix_signal_class); object_class->finalize = dex_unix_signal_finalize; } static void dex_unix_signal_init (DexUnixSignal *unix_signal) { } static gboolean dex_unix_signal_source_func (gpointer data) { DexWeakRef *wr = data; DexUnixSignal *unix_signal = dex_weak_ref_get (wr); g_assert (!unix_signal || DEX_IS_UNIX_SIGNAL (unix_signal)); if (unix_signal != NULL) { GValue value = G_VALUE_INIT; g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, unix_signal->signum); dex_future_complete (DEX_FUTURE (unix_signal), &value, NULL); } return G_SOURCE_REMOVE; } static void clear_weak_ref (gpointer data) { dex_weak_ref_clear (data); g_free (data); } /** * dex_unix_signal_new: * @signum: a unix signal number * * Creates a new #DexUnixSignal that completes when @signum is delivered * to the process. * * @signum must be one of SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, or * SIGWINCH. * * This API is only supported on UNIX-like systems. * * Returns: (transfer full): a new #DexFuture */ DexFuture * dex_unix_signal_new (int signum) { DexUnixSignal *unix_signal; const char *name = NULL; DexWeakRef *wr; g_return_val_if_fail (signum == SIGHUP || signum == SIGINT || signum == SIGTERM || signum == SIGUSR1 || signum == SIGUSR2 || signum == SIGWINCH, NULL); switch (signum) { case SIGHUP: name = "[dex-unix-signal-SIGHUP]"; break; case SIGINT: name = "[dex-unix-signal-SIGINT]"; break; case SIGTERM: name = "[dex-unix-signal-SIGTERM]"; break; case SIGUSR1: name = "[dex-unix-signal-SIGUSR1]"; break; case SIGUSR2: name = "[dex-unix-signal-SIGUSR2]"; break; case SIGWINCH: name = "[dex-unix-signal-SIGWINCH]"; break; default: g_assert_not_reached (); } unix_signal = (DexUnixSignal *)dex_object_create_instance (DEX_TYPE_UNIX_SIGNAL); unix_signal->signum = signum; unix_signal->source = g_unix_signal_source_new (signum); wr = g_new0 (DexWeakRef, 1); dex_weak_ref_init (wr, unix_signal); g_source_set_callback (unix_signal->source, dex_unix_signal_source_func, wr, clear_weak_ref); _g_source_set_static_name (unix_signal->source, name); g_source_attach (unix_signal->source, NULL); return DEX_FUTURE (unix_signal); } 0707010000006C000081A40000000000000000000000016712D33C000005E4000000000000000000000000000000000000002300000000libdex-0.8.1/src/dex-unix-signal.h/* * dex-unix-signal.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_UNIX_SIGNAL (dex_unix_signal_get_type()) #define DEX_UNIX_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_UNIX_SIGNAL, DexUnixSignal)) #define DEX_IS_UNIX_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_UNIX_SIGNAL)) typedef struct _DexUnixSignal DexUnixSignal; DEX_AVAILABLE_IN_ALL GType dex_unix_signal_get_type (void) G_GNUC_CONST; DEX_AVAILABLE_IN_ALL DexFuture *dex_unix_signal_new (int signum) G_GNUC_WARN_UNUSED_RESULT; DEX_AVAILABLE_IN_ALL int dex_unix_signal_get_signum (DexUnixSignal *unix_signal); G_DEFINE_AUTOPTR_CLEANUP_FUNC (DexUnixSignal, dex_unref) G_END_DECLS 0707010000006D000081A40000000000000000000000016712D33C00000593000000000000000000000000000000000000003100000000libdex-0.8.1/src/dex-uring-aio-backend-private.h/* dex-uring-aio-backend.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-aio-backend-private.h" G_BEGIN_DECLS #define DEX_TYPE_URING_AIO_BACKEND (dex_uring_aio_backend_get_type()) #define DEX_URING_AIO_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_URING_AIO_BACKEND, DexUringAioBackend)) #define DEX_IS_URING_AIO_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_URING_AIO_BACKEND)) typedef struct _DexUringAioBackend DexUringAioBackend; typedef struct _DexUringAioBackendClass DexUringAioBackendClass; GType dex_uring_aio_backend_get_type (void) G_GNUC_CONST; DexAioBackend *dex_uring_aio_backend_new (void); G_END_DECLS 0707010000006E000081A40000000000000000000000016712D33C00002BB4000000000000000000000000000000000000002900000000libdex-0.8.1/src/dex-uring-aio-backend.c/* dex-uring-aio-backend.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <errno.h> #include <sys/eventfd.h> #include <sys/utsname.h> #include <stdio.h> #include <unistd.h> #include <liburing.h> #include "dex-thread-storage-private.h" #include "dex-uring-aio-backend-private.h" #include "dex-uring-future-private.h" #include "dex-uring-version.h" #define DEFAULT_URING_SIZE 32 struct _DexUringAioBackend { DexAioBackend parent_instance; }; struct _DexUringAioBackendClass { DexAioBackendClass parent_class; }; typedef struct _DexUringAioContext { DexAioContext parent; struct io_uring ring; int eventfd; gpointer eventfdtag; GMutex mutex; GQueue queued; guint ring_initialized : 1; } DexUringAioContext; DEX_DEFINE_FINAL_TYPE (DexUringAioBackend, dex_uring_aio_backend, DEX_TYPE_AIO_BACKEND) G_GNUC_UNUSED static gboolean dex_uring_check_kernel_version (int major, int minor) { static gsize initialized; static int kernel_major; static int kernel_minor; if (g_once_init_enter (&initialized)) { static struct utsname u; if (uname (&u) == 0) { int maj, min; if (sscanf (u.release, "%u.%u.", &maj, &min) == 2) { kernel_major = maj; kernel_minor = min; } } g_once_init_leave (&initialized, TRUE); } if (kernel_major == 0) return FALSE; return kernel_major >= major || (kernel_major == major && kernel_minor >= minor); } static gboolean dex_uring_aio_context_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { DexUringAioContext *aio_context = (DexUringAioContext *)source; DexUringFuture *handledstack[32]; struct io_uring_cqe *cqe; gint64 counter; guint n_handled; if (g_source_query_unix_fd (source, aio_context->eventfdtag) & G_IO_IN) { if (read (aio_context->eventfd, &counter, sizeof counter) <= 0) { /* Do mothing */ } } again: n_handled = 0; while (io_uring_peek_cqe (&aio_context->ring, &cqe) == 0) { DexUringFuture *future = io_uring_cqe_get_data (cqe); dex_uring_future_cqe (future, cqe); io_uring_cqe_seen (&aio_context->ring, cqe); handledstack[n_handled++] = future; if G_UNLIKELY (n_handled == G_N_ELEMENTS (handledstack)) break; } for (guint i = 0; i < n_handled; i++) { DexUringFuture *future = handledstack[i]; dex_uring_future_complete (future); dex_unref (future); } if G_UNLIKELY (n_handled == G_N_ELEMENTS (handledstack)) goto again; return G_SOURCE_CONTINUE; } static gboolean dex_uring_aio_context_prepare (GSource *source, int *timeout) { DexUringAioContext *aio_context = (DexUringAioContext *)source; gboolean do_submit; g_assert (aio_context != NULL); g_assert (DEX_IS_URING_AIO_BACKEND (aio_context->parent.aio_backend)); *timeout = -1; g_mutex_lock (&aio_context->mutex); do_submit = aio_context->queued.length > 0; while (aio_context->queued.length) { struct io_uring_sqe *sqe; DexUringFuture *future; /* Try to get the next sqe, and submit if we can't get * one right away. If we still fail to get an sqe, then * we'll wait for completions to come in to advance this. */ if G_UNLIKELY (!(sqe = io_uring_get_sqe (&aio_context->ring))) { io_uring_submit (&aio_context->ring); if (!(sqe = io_uring_get_sqe (&aio_context->ring))) break; } future = g_queue_pop_head (&aio_context->queued); dex_uring_future_sqe (future, sqe); io_uring_sqe_set_data (sqe, dex_ref (future)); } if (do_submit || io_uring_sq_ready (&aio_context->ring) > 0) io_uring_submit (&aio_context->ring); g_mutex_unlock (&aio_context->mutex); return io_uring_cq_ready (&aio_context->ring) > 0; } static gboolean dex_uring_aio_context_check (GSource *source) { DexUringAioContext *aio_context = (DexUringAioContext *)source; g_assert (aio_context != NULL); g_assert (DEX_IS_URING_AIO_BACKEND (aio_context->parent.aio_backend)); return io_uring_cq_ready (&aio_context->ring) > 0; } static void dex_uring_aio_context_finalize (GSource *source) { DexUringAioContext *aio_context = (DexUringAioContext *)source; g_assert (aio_context != NULL); g_assert (DEX_IS_URING_AIO_BACKEND (aio_context->parent.aio_backend)); if (aio_context->queued.length > 0) g_critical ("Destroying DexAioContext with queued items!"); if (aio_context->ring_initialized) io_uring_queue_exit (&aio_context->ring); dex_clear (&aio_context->parent.aio_backend); g_mutex_clear (&aio_context->mutex); if (aio_context->eventfd != -1) { close (aio_context->eventfd); aio_context->eventfd = -1; } } static GSourceFuncs dex_uring_aio_context_source_funcs = { .check = dex_uring_aio_context_check, .prepare = dex_uring_aio_context_prepare, .dispatch = dex_uring_aio_context_dispatch, .finalize = dex_uring_aio_context_finalize, }; static DexFuture * dex_uring_aio_context_queue (DexUringAioContext *aio_context, DexUringFuture *future) { gboolean is_same_thread; struct io_uring_sqe *sqe; g_assert (aio_context != NULL); g_assert (DEX_IS_URING_AIO_BACKEND (aio_context->parent.aio_backend)); g_assert (DEX_IS_URING_FUTURE (future)); is_same_thread = dex_thread_storage_get ()->aio_context == (DexAioContext *)aio_context; g_mutex_lock (&aio_context->mutex); if G_LIKELY (is_same_thread && aio_context->queued.length == 0 && (sqe = io_uring_get_sqe (&aio_context->ring))) { dex_uring_future_sqe (future, sqe); io_uring_sqe_set_data (sqe, dex_ref (future)); } else { g_queue_push_tail (&aio_context->queued, dex_ref (future)); } g_mutex_unlock (&aio_context->mutex); if (!is_same_thread) g_main_context_wakeup (g_source_get_context ((GSource *)aio_context)); return DEX_FUTURE (future); } static DexAioContext * dex_uring_aio_backend_create_context (DexAioBackend *aio_backend) { DexUringAioContext *aio_context; guint uring_flags = 0; g_assert (DEX_IS_URING_AIO_BACKEND (aio_backend)); aio_context = (DexUringAioContext *) g_source_new (&dex_uring_aio_context_source_funcs, sizeof *aio_context); g_source_set_can_recurse ((GSource *)aio_context, TRUE); aio_context->parent.aio_backend = dex_ref (aio_backend); g_mutex_init (&aio_context->mutex); #if DEX_URING_CHECK_VERSION(2, 2) if (dex_uring_check_kernel_version (5, 19)) uring_flags |= IORING_SETUP_COOP_TASKRUN; #endif #if DEX_URING_CHECK_VERSION(2, 3) if (dex_uring_check_kernel_version (6, 0)) uring_flags |= IORING_SETUP_SINGLE_ISSUER; #endif aio_context->eventfd = -1; /* Setup uring submission/completion queue */ if (io_uring_queue_init (DEFAULT_URING_SIZE, &aio_context->ring, uring_flags) != 0) goto failure; aio_context->ring_initialized = TRUE; #if DEX_URING_CHECK_VERSION(2, 2) /* Register the ring FD so we don't have to on every io_ring_enter() */ if (io_uring_register_ring_fd (&aio_context->ring) < 0) goto failure; #endif /* Create eventfd() we can poll() on with GMainContext since GMainContext * knows nothing of uring and how to drive the loop using that. */ if (-1 == (aio_context->eventfd = eventfd (0, EFD_CLOEXEC)) || io_uring_register_eventfd (&aio_context->ring, aio_context->eventfd) != 0) goto failure; /* Add the eventfd() to our set of pollfds and keep the tag around so * we can check the condition directly. */ aio_context->eventfdtag = g_source_add_unix_fd ((GSource *)aio_context, aio_context->eventfd, G_IO_IN); return (DexAioContext *)aio_context; failure: g_source_unref ((GSource *)aio_context); return NULL; } static DexFuture * dex_uring_aio_backend_read (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gpointer buffer, gsize count, goffset offset) { return dex_uring_aio_context_queue ((DexUringAioContext *)aio_context, dex_uring_future_new_read (fd, buffer, count, offset)); } static DexFuture * dex_uring_aio_backend_write (DexAioBackend *aio_backend, DexAioContext *aio_context, int fd, gconstpointer buffer, gsize count, goffset offset) { return dex_uring_aio_context_queue ((DexUringAioContext *)aio_context, dex_uring_future_new_write (fd, buffer, count, offset)); } static void dex_uring_aio_backend_class_init (DexUringAioBackendClass *uring_aio_backend_class) { DexAioBackendClass *aio_backend_class = DEX_AIO_BACKEND_CLASS (uring_aio_backend_class); aio_backend_class->create_context = dex_uring_aio_backend_create_context; aio_backend_class->read = dex_uring_aio_backend_read; aio_backend_class->write = dex_uring_aio_backend_write; } static void dex_uring_aio_backend_init (DexUringAioBackend *uring_aio_backend) { } DexAioBackend * dex_uring_aio_backend_new (void) { DexAioBackend *aio_backend; DexAioContext *aio_context; /* We run into a number of issues with io_uring on older kernels which * makes it hard to detect up-front if things will work. So just bail * out if we have a kernel older than 6.1. * * See https://gitlab.gnome.org/GNOME/libdex/-/issues/17 */ if (!dex_uring_check_kernel_version (6, 1)) return NULL; aio_backend = (DexAioBackend *)dex_object_create_instance (DEX_TYPE_URING_AIO_BACKEND); /* Make sure we are capable of creating an aio_context */ if (!(aio_context = dex_aio_backend_create_context (aio_backend))) { dex_unref (aio_backend); return NULL; } g_source_unref ((GSource *)aio_context); return aio_backend; } 0707010000006F000081A40000000000000000000000016712D33C000008B3000000000000000000000000000000000000002C00000000libdex-0.8.1/src/dex-uring-future-private.h/* dex-uring-future-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <liburing.h> #include "dex-future.h" G_BEGIN_DECLS #define DEX_TYPE_URING_FUTURE (dex_uring_future_get_type()) #define DEX_URING_FUTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_URING_FUTURE, DexUringFuture)) #define DEX_IS_URING_FUTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_URING_FUTURE)) typedef struct _DexUringFuture DexUringFuture; GType dex_uring_future_get_type (void) G_GNUC_CONST; DexUringFuture *dex_uring_future_new_read (int fd, gpointer buffer, gsize count, goffset offset); DexUringFuture *dex_uring_future_new_write (int fd, gconstpointer buffer, gsize count, goffset offset); void dex_uring_future_sqe (DexUringFuture *uring_future, struct io_uring_sqe *sqe); void dex_uring_future_cqe (DexUringFuture *uring_future, struct io_uring_cqe *cqe); void dex_uring_future_complete (DexUringFuture *uring_future); G_END_DECLS 07070100000070000081A40000000000000000000000016712D33C00001340000000000000000000000000000000000000002400000000libdex-0.8.1/src/dex-uring-future.c/* dex-uring-future.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <liburing.h> #include <gio/gio.h> #include "dex-future-private.h" #include "dex-uring-future-private.h" typedef enum _DexUringType { DEX_URING_TYPE_READ = 1, DEX_URING_TYPE_WRITE, } DexUringType; struct _DexUringFuture { DexFuture parent_instance; DexUringType type; union { struct { int fd; gpointer buffer; gsize count; goffset offset; gssize result; } read; struct { int fd; gconstpointer buffer; gsize count; goffset offset; gssize result; } write; }; }; typedef struct _DexUringFutureClass { DexFutureClass parent_class; } DexUringFutureClass; DEX_DEFINE_FINAL_TYPE (DexUringFuture, dex_uring_future, DEX_TYPE_FUTURE) #undef DEX_TYPE_URING_FUTURE #define DEX_TYPE_URING_FUTURE dex_uring_future_type static void dex_uring_future_class_init (DexUringFutureClass *uring_future_class) { } static void dex_uring_future_init (DexUringFuture *uring_future) { } static GError * create_error (int error_code) { return g_error_new_literal (G_IO_ERROR, g_io_error_from_errno (-error_code), g_strerror (-error_code)); } static void complete_ssize (DexUringFuture *uring_future, gssize value) { if (value < 0) dex_future_complete (DEX_FUTURE (uring_future), NULL, create_error (-value)); else dex_future_complete (DEX_FUTURE (uring_future), &(GValue) { G_TYPE_INT64, {{.v_int64 = value}}}, NULL); } void dex_uring_future_complete (DexUringFuture *uring_future) { switch (uring_future->type) { case DEX_URING_TYPE_READ: complete_ssize (uring_future, uring_future->read.result); break; case DEX_URING_TYPE_WRITE: complete_ssize (uring_future, uring_future->write.result); break; default: g_assert_not_reached (); } } void dex_uring_future_cqe (DexUringFuture *uring_future, struct io_uring_cqe *cqe) { switch (uring_future->type) { case DEX_URING_TYPE_READ: uring_future->read.result = cqe->res; break; case DEX_URING_TYPE_WRITE: uring_future->write.result = cqe->res; break; default: g_assert_not_reached (); } } void dex_uring_future_sqe (DexUringFuture *uring_future, struct io_uring_sqe *sqe) { switch (uring_future->type) { case DEX_URING_TYPE_READ: io_uring_prep_read (sqe, uring_future->read.fd, uring_future->read.buffer, uring_future->read.count, uring_future->read.offset); break; case DEX_URING_TYPE_WRITE: io_uring_prep_write (sqe, uring_future->write.fd, uring_future->write.buffer, uring_future->write.count, uring_future->write.offset); break; default: g_assert_not_reached (); } } DexUringFuture * dex_uring_future_new_read (int fd, gpointer buffer, gsize count, goffset offset) { DexUringFuture *future; future = (DexUringFuture *)dex_object_create_instance (DEX_TYPE_URING_FUTURE); future->type = DEX_URING_TYPE_READ; future->read.fd = fd; future->read.buffer = buffer; future->read.count = count; future->read.offset = offset; return future; } DexUringFuture * dex_uring_future_new_write (int fd, gconstpointer buffer, gsize count, goffset offset) { DexUringFuture *future; future = (DexUringFuture *)dex_object_create_instance (DEX_TYPE_URING_FUTURE); future->type = DEX_URING_TYPE_WRITE; future->write.fd = fd; future->write.buffer = buffer; future->write.count = count; future->write.offset = offset; return future; } 07070100000071000081A40000000000000000000000016712D33C0000013A000000000000000000000000000000000000002800000000libdex-0.8.1/src/dex-uring-version.h.in#pragma once #define DEX_URING_MAJOR_VERSION (@DEX_URING_MAJOR_VERSION@) #define DEX_URING_MINOR_VERSION (@DEX_URING_MINOR_VERSION@) #define DEX_URING_CHECK_VERSION(major,minor) \ (DEX_URING_MAJOR_VERSION > (major) || \ (DEX_URING_MAJOR_VERSION == (major) && DEX_URING_MINOR_VERSION >= (minor))) 07070100000072000081A40000000000000000000000016712D33C00000D15000000000000000000000000000000000000002600000000libdex-0.8.1/src/dex-version-macros.h/* * dex-version-macros.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> #include "dex-version.h" #ifndef _DEX_EXTERN # define _DEX_EXTERN extern #endif #define DEX_VERSION_CUR_STABLE (G_ENCODE_VERSION (DEX_MAJOR_VERSION, 0)) #ifdef DEX_DISABLE_DEPRECATION_WARNINGS # define DEX_DEPRECATED _DEX_EXTERN # define DEX_DEPRECATED_FOR(f) _DEX_EXTERN # define DEX_UNAVAILABLE(maj,min) _DEX_EXTERN #else # define DEX_DEPRECATED G_DEPRECATED _DEX_EXTERN # define DEX_DEPRECATED_FOR(f) G_DEPRECATED_FOR (f) _DEX_EXTERN # define DEX_UNAVAILABLE(maj,min) G_UNAVAILABLE (maj, min) _DEX_EXTERN #endif #define DEX_VERSION_1_0 (G_ENCODE_VERSION (1, 0)) #if DEX_MAJOR_VERSION == DEX_VERSION_1_0 # define DEX_VERSION_PREV_STABLE (DEX_VERSION_1_0) #else # define DEX_VERSION_PREV_STABLE (G_ENCODE_VERSION (DEX_MAJOR_VERSION - 1, 0)) #endif /** * DEX_VERSION_MIN_REQUIRED: * * A macro that should be defined by the user prior to including * the dex.h header. * * The definition should be one of the predefined DEX version * macros: %DEX_VERSION_1_0, ... * * This macro defines the lower bound for the Builder API to use. * * If a function has been deprecated in a newer version of Builder, * it is possible to use this symbol to avoid the compiler warnings * without disabling warning for every deprecated function. */ #ifndef DEX_VERSION_MIN_REQUIRED # define DEX_VERSION_MIN_REQUIRED (DEX_VERSION_CUR_STABLE) #endif /** * DEX_VERSION_MAX_ALLOWED: * * A macro that should be defined by the user prior to including * the dex.h header. * The definition should be one of the predefined Builder version * macros: %DEX_VERSION_1_0, %DEX_VERSION_1_2,... * * This macro defines the upper bound for the DEX API to use. * * If a function has been introduced in a newer version of Builder, * it is possible to use this symbol to get compiler warnings when * trying to use that function. */ #ifndef DEX_VERSION_MAX_ALLOWED # if DEX_VERSION_MIN_REQUIRED > DEX_VERSION_PREV_STABLE # define DEX_VERSION_MAX_ALLOWED (DEX_VERSION_MIN_REQUIRED) # else # define DEX_VERSION_MAX_ALLOWED (DEX_VERSION_CUR_STABLE) # endif #endif #define DEX_AVAILABLE_IN_ALL _DEX_EXTERN #if DEX_VERSION_MIN_REQUIRED >= DEX_VERSION_1_0 # define DEX_DEPRECATED_IN_1_0 DEX_DEPRECATED # define DEX_DEPRECATED_IN_1_0_FOR(f) DEX_DEPRECATED_FOR(f) #else # define DEX_DEPRECATED_IN_1_0 _DEX_EXTERN # define DEX_DEPRECATED_IN_1_0_FOR(f) _DEX_EXTERN #endif #if DEX_VERSION_MAX_ALLOWED < DEX_VERSION_1_0 # define DEX_AVAILABLE_IN_1_0 DEX_UNAVAILABLE(1, 0) #else # define DEX_AVAILABLE_IN_1_0 _DEX_EXTERN #endif 07070100000073000081A40000000000000000000000016712D33C00000A7B000000000000000000000000000000000000002200000000libdex-0.8.1/src/dex-version.h.in/* dex-version.h.in * * Copyright 2022 Christian Hergert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined(DEX_INSIDE) && !defined(DEX_COMPILATION) # error "Only <libdex.h> can be included directly." #endif /** * SECTION:libdexversion * @short_description: libdex version checking * * libdex provides macros to check the version of the library * at compile-time */ /** * DEX_MAJOR_VERSION: * * libdex major version component (e.g. 1 if %DEX_VERSION is 1.2.3) */ #define DEX_MAJOR_VERSION (@MAJOR_VERSION@) /** * DEX_MINOR_VERSION: * * libdex minor version component (e.g. 2 if %DEX_VERSION is 1.2.3) */ #define DEX_MINOR_VERSION (@MINOR_VERSION@) /** * DEX_MICRO_VERSION: * * libdex micro version component (e.g. 3 if %DEX_VERSION is 1.2.3) */ #define DEX_MICRO_VERSION (@MICRO_VERSION@) /** * DEX_VERSION * * libdex version. */ #define DEX_VERSION (@VERSION@) /** * DEX_VERSION_S: * * libdex version, encoded as a string, useful for printing and * concatenation. */ #define DEX_VERSION_S "@VERSION@" #define DEX_ENCODE_VERSION(major,minor,micro) \ ((major) << 24 | (minor) << 16 | (micro) << 8) /** * DEX_VERSION_HEX: * * libdex version, encoded as an hexadecimal number, useful for * integer comparisons. */ #define DEX_VERSION_HEX \ (DEX_ENCODE_VERSION (DEX_MAJOR_VERSION, DEX_MINOR_VERSION, DEX_MICRO_VERSION)) /** * DEX_CHECK_VERSION: * @major: required major version * @minor: required minor version * @micro: required micro version * * Compile-time version checking. Evaluates to %TRUE if the version * of libdex is greater than the required one. */ #define DEX_CHECK_VERSION(major,minor,micro) \ (DEX_MAJOR_VERSION > (major) || \ (DEX_MAJOR_VERSION == (major) && DEX_MINOR_VERSION > (minor)) || \ (DEX_MAJOR_VERSION == (major) && DEX_MINOR_VERSION == (minor) && \ DEX_MICRO_VERSION >= (micro))) 07070100000074000081A40000000000000000000000016712D33C0000064D000000000000000000000000000000000000002A00000000libdex-0.8.1/src/dex-work-queue-private.h/* * dex-work-queue-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "dex-scheduler-private.h" G_BEGIN_DECLS #define DEX_TYPE_WORK_QUEUE (dex_work_queue_get_type()) #define DEX_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, DEX_TYPE_WORK_QUEUE, DexWorkQueue)) #define DEX_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, DEX_TYPE_WORK_QUEUE)) typedef struct _DexWorkQueue DexWorkQueue; GType dex_work_queue_get_type (void) G_GNUC_CONST; DexWorkQueue *dex_work_queue_new (void); void dex_work_queue_push (DexWorkQueue *work_queue, DexWorkItem work_item); gboolean dex_work_queue_try_pop (DexWorkQueue *work_queue, DexWorkItem *out_work_item); DexFuture *dex_work_queue_run (DexWorkQueue *work_queue); G_END_DECLS 07070100000075000081A40000000000000000000000016712D33C00001009000000000000000000000000000000000000002200000000libdex-0.8.1/src/dex-work-queue.c/* * dex-work-queue.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-object-private.h" #include "dex-semaphore-private.h" #include "dex-work-queue-private.h" struct _DexWorkQueue { DexObject parent_instance; DexSemaphore *semaphore; GMutex mutex; GQueue queue; }; typedef struct _DexWorkQueueClass { DexObjectClass parent_class; } DexWorkQueueClass; DEX_DEFINE_FINAL_TYPE (DexWorkQueue, dex_work_queue, DEX_TYPE_OBJECT) typedef struct _DexWorkQueueItem { GList link; DexWorkItem work_item; } DexWorkQueueItem; static void dex_work_queue_finalize (DexObject *object) { DexWorkQueue *work_queue = DEX_WORK_QUEUE (object); if (work_queue->queue.length > 0) g_critical ("Work queue %p freed with %u items still in it!", work_queue, work_queue->queue.length); g_mutex_clear (&work_queue->mutex); dex_clear (&work_queue->semaphore); DEX_OBJECT_CLASS (dex_work_queue_parent_class)->finalize (object); } static void dex_work_queue_class_init (DexWorkQueueClass *work_queue_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (work_queue_class); object_class->finalize = dex_work_queue_finalize; } static void dex_work_queue_init (DexWorkQueue *work_queue) { work_queue->semaphore = dex_semaphore_new (); g_mutex_init (&work_queue->mutex); } DexWorkQueue * dex_work_queue_new (void) { return (DexWorkQueue *)dex_object_create_instance (DEX_TYPE_WORK_QUEUE); } void dex_work_queue_push (DexWorkQueue *work_queue, DexWorkItem work_item) { DexWorkQueueItem *work_queue_item; g_return_if_fail (DEX_IS_WORK_QUEUE (work_queue)); g_return_if_fail (work_item.func != NULL); work_queue_item = g_new0 (DexWorkQueueItem, 1); work_queue_item->link.data = work_queue_item; work_queue_item->work_item = work_item; g_mutex_lock (&work_queue->mutex); g_queue_push_tail_link (&work_queue->queue, &work_queue_item->link); g_mutex_unlock (&work_queue->mutex); dex_semaphore_post (work_queue->semaphore); } gboolean dex_work_queue_try_pop (DexWorkQueue *work_queue, DexWorkItem *out_work_item) { GList *link; g_return_val_if_fail (DEX_IS_WORK_QUEUE (work_queue), FALSE); g_return_val_if_fail (out_work_item != NULL, FALSE); g_mutex_lock (&work_queue->mutex); link = g_queue_pop_head_link (&work_queue->queue); g_mutex_unlock (&work_queue->mutex); if (link != NULL) { DexWorkQueueItem *item = link->data; *out_work_item = item->work_item; g_free (item); } return link != NULL; } static DexFuture * dex_work_queue_run_cb (DexFuture *future, gpointer user_data) { DexWorkQueue *work_queue = user_data; DexWorkItem work_item; g_assert (DEX_IS_WORK_QUEUE (work_queue)); if (dex_work_queue_try_pop (work_queue, &work_item)) dex_work_item_invoke (&work_item); return dex_semaphore_wait (work_queue->semaphore); } DexFuture * dex_work_queue_run (DexWorkQueue *work_queue) { DexFuture *future; g_return_val_if_fail (work_queue != NULL, NULL); future = dex_semaphore_wait (work_queue->semaphore); future = dex_future_finally_loop (future, dex_work_queue_run_cb, dex_ref (work_queue), dex_unref); return future; } 07070100000076000081A40000000000000000000000016712D33C00002BB5000000000000000000000000000000000000003300000000libdex-0.8.1/src/dex-work-stealing-queue-private.h/* * dex-work-stealing-queue-private.h * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ /* This code is heavily based upon wsq.hpp¹ from Tsung-Wei Huang, under * the MIT license. Its original license is provided below. * * ¹ https://github.com/taskflow/work-stealing-queue/tree/master/wsq.hpp * * MIT License * * Copyright (c) 2020 T.-W. Huang * * University of Utah, Salt Lake City, UT, USA * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #pragma once #include <stdatomic.h> #include <glib.h> #include "dex-scheduler-private.h" /** * SECTION:dex-work-stealing-queue * @title: DexWorkStealinQueue * @short_description: Lock-free unbounded single-producer multiple consumer queue * * This class implements the work stealing queue described in the paper, * "Correct and Efficient Work-Stealing for Weak Memory Models," available at * https://www.di.ens.fr/~zappa/readings/ppopp13.pdf * * Only the queue owner can perform pop and push operations, while others can * steal data from the queue. */ G_BEGIN_DECLS #ifndef DEX_CACHELINE_SIZE # define DEX_CACHELINE_SIZE 64 #endif typedef struct _DexWorkStealingArray { gint64 C; gint64 M; _Atomic(DexWorkItem) S[]; } DexWorkStealingArray; typedef struct _DexWorkStealingQueue { _Alignas(DEX_CACHELINE_SIZE) _Atomic(gint64) top; _Alignas(DEX_CACHELINE_SIZE) _Atomic(gint64) bottom; _Alignas(DEX_CACHELINE_SIZE) _Atomic(DexWorkStealingArray*) array; GPtrArray *garbage; gatomicrefcount ref_count; } DexWorkStealingQueue; DexWorkStealingQueue *dex_work_stealing_queue_new (gint64 capacity); DexWorkStealingQueue *dex_work_stealing_queue_ref (DexWorkStealingQueue *work_stealing_queue); void dex_work_stealing_queue_unref (DexWorkStealingQueue *work_stealing_queue); GSource *dex_work_stealing_queue_create_source (DexWorkStealingQueue *work_stealing_queue); static inline DexWorkStealingArray * dex_work_stealing_array_new (gint64 c) { DexWorkStealingArray *work_stealing_array; work_stealing_array = g_aligned_alloc0 (1, sizeof (DexWorkStealingArray) + (c * sizeof (DexWorkItem)), G_ALIGNOF (DexWorkStealingArray)); work_stealing_array->C = c; work_stealing_array->M = c-1; return work_stealing_array; } static inline gint64 dex_work_stealing_array_capacity (DexWorkStealingArray *work_stealing_array) { return work_stealing_array->C; } static inline void dex_work_stealing_array_push (DexWorkStealingArray *work_stealing_array, gint64 i, DexWorkItem work_item) { atomic_store_explicit (&work_stealing_array->S[i & work_stealing_array->M], work_item, memory_order_relaxed); } static inline DexWorkItem dex_work_stealing_array_pop (DexWorkStealingArray *work_stealing_array, gint64 i) { return atomic_load_explicit (&work_stealing_array->S[i & work_stealing_array->M], memory_order_relaxed); } static inline DexWorkStealingArray * dex_work_stealing_array_resize (DexWorkStealingArray *work_stealing_array, gint64 b, gint64 t) { DexWorkStealingArray *ptr = dex_work_stealing_array_new (work_stealing_array->C * 2); for (gint64 i = t; i != b; i++) dex_work_stealing_array_push (ptr, i, dex_work_stealing_array_pop (work_stealing_array, i)); return ptr; } static inline void dex_work_stealing_array_free (DexWorkStealingArray *work_stealing_array) { g_aligned_free (work_stealing_array); } static inline gboolean dex_work_stealing_queue_empty (DexWorkStealingQueue *work_stealing_queue) { gint64 b = atomic_load_explicit (&work_stealing_queue->bottom, memory_order_relaxed); gint64 t = atomic_load_explicit (&work_stealing_queue->top, memory_order_relaxed); return b <= t; } static inline gsize dex_work_stealing_queue_size (DexWorkStealingQueue *work_stealing_queue) { gint64 b = atomic_load_explicit (&work_stealing_queue->bottom, memory_order_relaxed); gint64 t = atomic_load_explicit (&work_stealing_queue->top, memory_order_relaxed); return b >= t ? b - t : 0; } /** * dex_work_stealing_queue_push: * @work_stealing_queue: a #DexWorkStealingQueue * @work_item: the work item to push * * This adds @work_item to the queue so that it can be processed by the * local worker, or optionally, stolen by another worker after there are * no more items to process in its queue or the global queue. * * This may _ONLY_ be called by the thread that owns @work_stealing_queue. * * Work items that originate outside of a worker thread should be pushed into * a global queue shared among workers. */ static inline void dex_work_stealing_queue_push (DexWorkStealingQueue *work_stealing_queue, DexWorkItem work_item) { gint64 b = atomic_load_explicit (&work_stealing_queue->bottom, memory_order_relaxed); gint64 t = atomic_load_explicit (&work_stealing_queue->top, memory_order_acquire); DexWorkStealingArray *a = atomic_load_explicit (&work_stealing_queue->array, memory_order_relaxed); /* queue is full */ if (dex_work_stealing_array_capacity (a) - 1 < (b - t)) { DexWorkStealingArray *tmp = dex_work_stealing_array_resize (a, b, t); g_ptr_array_add (work_stealing_queue->garbage, a); a = tmp; atomic_store_explicit (&work_stealing_queue->array, a, memory_order_relaxed); } dex_work_stealing_array_push (a, b, work_item); atomic_thread_fence (memory_order_release); atomic_store_explicit (&work_stealing_queue->bottom, b + 1, memory_order_relaxed); } /** * dex_work_stealing_queue_pop: * @work_stealing_queue: a #DexWorkStealingQueue * @out_work_item: (out): a location for a #DexWorkItem * * Pops the next work item from the queue without synchronization. * * This may _ONLY_ be called by the thread that owns @work_stealing_queue. * * Returns: %TRUE if @out_work_item was set, otherwise %FALSE and the queue * is currently empty. */ static inline gboolean dex_work_stealing_queue_pop (DexWorkStealingQueue *work_stealing_queue, DexWorkItem *out_work_item) { DexWorkStealingArray *a; gboolean ret; gint64 b; gint64 t; b = atomic_load_explicit (&work_stealing_queue->bottom, memory_order_relaxed) - 1; a = atomic_load_explicit (&work_stealing_queue->array, memory_order_relaxed); atomic_store_explicit (&work_stealing_queue->bottom, b, memory_order_relaxed); atomic_thread_fence (memory_order_seq_cst); t = atomic_load_explicit (&work_stealing_queue->top, memory_order_relaxed); ret = t <= b; if (ret) { *out_work_item = dex_work_stealing_array_pop (a, b); if (t == b) { /* the last item just got stolen */ if (!atomic_compare_exchange_strong_explicit (&work_stealing_queue->top, &t, t + 1, memory_order_seq_cst, memory_order_relaxed)) ret = FALSE; atomic_store_explicit (&work_stealing_queue->bottom, b + 1, memory_order_relaxed); } } else { atomic_store_explicit (&work_stealing_queue->bottom, b + 1, memory_order_relaxed); } return ret; } /** * dex_work_stealing_queue_steal: * @work_stealing_queue: a #DexWorkStealingQueue * @out_work_item: (out): a location to store the work item * * Attempts to steal a #DexWorkItem from @work_stealing_queue with lock-free * synchronization among workers. * * This function is _ONLY_ be called by threads other than the thread owning * @work_stealing_queue. * * Returns: %TRUE if @out_work_item is set; otherwise %FALSE */ static inline gboolean dex_work_stealing_queue_steal (DexWorkStealingQueue *work_stealing_queue, DexWorkItem *out_work_item) { gint64 t; gint64 b; t = atomic_load_explicit (&work_stealing_queue->top, memory_order_acquire); atomic_thread_fence (memory_order_seq_cst); b = atomic_load_explicit (&work_stealing_queue->bottom, memory_order_acquire); if (t < b) { DexWorkStealingArray *a = atomic_load_explicit (&work_stealing_queue->array, memory_order_consume); *out_work_item = dex_work_stealing_array_pop (a, t); if (atomic_compare_exchange_strong_explicit (&work_stealing_queue->top, &t, t + 1, memory_order_seq_cst, memory_order_relaxed)) return TRUE; } return FALSE; } static inline gint64 dex_work_stealing_queue_capacity (DexWorkStealingQueue *work_stealing_queue) { return dex_work_stealing_array_capacity ( atomic_load_explicit (&work_stealing_queue->array, memory_order_relaxed)); } G_END_DECLS 07070100000077000081A40000000000000000000000016712D33C000012B4000000000000000000000000000000000000002B00000000libdex-0.8.1/src/dex-work-stealing-queue.c/* * dex-work-stealing-queue.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "dex-compat-private.h" #include "dex-work-stealing-queue-private.h" #define DEFAULT_BATCH_SIZE 32 DexWorkStealingQueue * dex_work_stealing_queue_new (gint64 capacity) { DexWorkStealingQueue *work_stealing_queue; work_stealing_queue = g_aligned_alloc0 (1, sizeof (DexWorkStealingQueue), G_ALIGNOF (DexWorkStealingQueue)); atomic_store_explicit (&work_stealing_queue->top, 0, memory_order_relaxed); atomic_store_explicit (&work_stealing_queue->bottom, 0, memory_order_relaxed); atomic_store_explicit (&work_stealing_queue->array, dex_work_stealing_array_new (capacity), memory_order_relaxed); work_stealing_queue->garbage = g_ptr_array_new_full (32, (GDestroyNotify)dex_work_stealing_array_free); g_atomic_ref_count_init (&work_stealing_queue->ref_count); return work_stealing_queue; } DexWorkStealingQueue * dex_work_stealing_queue_ref (DexWorkStealingQueue *work_stealing_queue) { g_atomic_ref_count_inc (&work_stealing_queue->ref_count); return work_stealing_queue; } void dex_work_stealing_queue_unref (DexWorkStealingQueue *work_stealing_queue) { if (g_atomic_ref_count_dec (&work_stealing_queue->ref_count)) { GPtrArray *garbage = work_stealing_queue->garbage; DexWorkStealingArray *array = atomic_load (&work_stealing_queue->array); work_stealing_queue->top = 0; work_stealing_queue->bottom = 0; work_stealing_queue->array = NULL; work_stealing_queue->garbage = NULL; g_clear_pointer (&garbage, g_ptr_array_unref); g_clear_pointer (&array, dex_work_stealing_array_free); g_aligned_free (work_stealing_queue); } } typedef struct _DexWorkStealingQueueSource { GSource parent_instance; DexWorkStealingQueue *work_stealing_queue; guint batch_size; } DexWorkStealingQueueSource; static gboolean dex_work_stealing_queue_source_prepare (GSource *source, int *timeout) { DexWorkStealingQueueSource *real_source = (DexWorkStealingQueueSource *)source; *timeout = -1; return !dex_work_stealing_queue_empty (real_source->work_stealing_queue); } static gboolean dex_work_stealing_queue_source_check (GSource *source) { DexWorkStealingQueueSource *real_source = (DexWorkStealingQueueSource *)source; return !dex_work_stealing_queue_empty (real_source->work_stealing_queue); } static gboolean dex_work_stealing_queue_source_dispatch (GSource *source, GSourceFunc callback, gpointer callback_data) { DexWorkStealingQueueSource *real_source = (DexWorkStealingQueueSource *)source; DexWorkStealingQueue *work_stealing_queue = real_source->work_stealing_queue; for (guint i = 0; i < real_source->batch_size; i++) { DexWorkItem work_item; if (!dex_work_stealing_queue_pop (work_stealing_queue, &work_item)) break; dex_work_item_invoke (&work_item); } return G_SOURCE_CONTINUE; } static GSourceFuncs dex_work_stealing_queue_source_funcs = { .prepare = dex_work_stealing_queue_source_prepare, .check = dex_work_stealing_queue_source_check, .dispatch = dex_work_stealing_queue_source_dispatch, }; GSource * dex_work_stealing_queue_create_source (DexWorkStealingQueue *work_stealing_queue) { DexWorkStealingQueueSource *real_source; GSource *source; g_return_val_if_fail (work_stealing_queue != NULL, NULL); source = g_source_new (&dex_work_stealing_queue_source_funcs, sizeof (DexWorkStealingQueueSource)); real_source = (DexWorkStealingQueueSource *)source; _g_source_set_static_name (source, "[dex-work-stealing-queue]"); real_source->work_stealing_queue = dex_work_stealing_queue_ref (work_stealing_queue); real_source->batch_size = DEFAULT_BATCH_SIZE; return source; } 07070100000078000081A40000000000000000000000016712D33C000015E5000000000000000000000000000000000000002000000000libdex-0.8.1/src/gconstructor.h/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ /* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. */ #ifndef __G_CONSTRUCTOR_H__ #define __G_CONSTRUCTOR_H__ /* If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and destructors, in a usable way, including e.g. on library unload. If not you're on your own. Some compilers need #pragma to handle this, which does not work with macros, so the way you need to use this is (for constructors): #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor) #endif G_DEFINE_CONSTRUCTOR(my_constructor) static void my_constructor(void) { ... } */ #ifndef __GTK_DOC_IGNORE__ #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) #define G_HAS_CONSTRUCTORS 1 #define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void); #define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void); #elif defined (_MSC_VER) && (_MSC_VER >= 1500) /* Visual studio 2008 and later has _Pragma */ /* * Only try to include gslist.h if not already included via glib.h, * so that items using gconstructor.h outside of GLib (such as * GResources) continue to build properly. */ #ifndef __G_LIB_H__ #include "gslist.h" #endif #include <stdlib.h> #define G_HAS_CONSTRUCTORS 1 /* We do some weird things to avoid the constructors being optimized * away on VS2015 if WholeProgramOptimization is enabled. First we * make a reference to the array from the wrapper to make sure its * references. Then we use a pragma to make sure the wrapper function * symbol is always included at the link stage. Also, the symbols * need to be extern (but not dllexport), even though they are not * really used from another object file. */ /* We need to account for differences between the mangling of symbols * for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed * with an underscore but symbols on x64/ARM/ARM64 are not. */ #ifdef _M_IX86 #define G_MSVC_SYMBOL_PREFIX "_" #else #define G_MSVC_SYMBOL_PREFIX "" #endif #define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX) #define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX) #define G_MSVC_CTOR(_func,_sym_prefix) \ static void _func(void); \ extern int (* _array ## _func)(void); \ int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \ __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper; #define G_MSVC_DTOR(_func,_sym_prefix) \ static void _func(void); \ extern int (* _array ## _func)(void); \ int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \ __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor; #elif defined (_MSC_VER) #define G_HAS_CONSTRUCTORS 1 /* Pre Visual studio 2008 must use #pragma section */ #define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ section(".CRT$XCU",read) #define G_DEFINE_CONSTRUCTOR(_func) \ static void _func(void); \ static int _func ## _wrapper(void) { _func(); return 0; } \ __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper; #define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ section(".CRT$XCU",read) #define G_DEFINE_DESTRUCTOR(_func) \ static void _func(void); \ static int _func ## _constructor(void) { atexit (_func); return 0; } \ __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; #elif defined(__SUNPRO_C) /* This is not tested, but i believe it should work, based on: * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c */ #define G_HAS_CONSTRUCTORS 1 #define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ init(_func) #define G_DEFINE_CONSTRUCTOR(_func) \ static void _func(void); #define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ fini(_func) #define G_DEFINE_DESTRUCTOR(_func) \ static void _func(void); #else /* constructors not supported for this compiler */ #endif #endif /* __GTK_DOC_IGNORE__ */ #endif /* __G_CONSTRUCTOR_H__ */ 07070100000079000081A40000000000000000000000016712D33C00000682000000000000000000000000000000000000001A00000000libdex-0.8.1/src/libdex.h/* * libdex.h * * Copyright 2022 Christian Hergert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib-object.h> G_BEGIN_DECLS #define DEX_INSIDE # include "dex-aio.h" # include "dex-async-pair.h" # include "dex-async-result.h" # include "dex-block.h" # include "dex-cancellable.h" # include "dex-channel.h" # include "dex-delayed.h" # include "dex-enums.h" # include "dex-error.h" # include "dex-fiber.h" # include "dex-future.h" # include "dex-future-set.h" # include "dex-gio.h" # include "dex-init.h" # include "dex-main-scheduler.h" # include "dex-object.h" # include "dex-platform.h" # include "dex-promise.h" # include "dex-scheduler.h" # include "dex-static-future.h" # include "dex-thread-pool-scheduler.h" # include "dex-timeout.h" #ifdef G_OS_UNIX # include "dex-unix-signal.h" #endif # include "dex-version.h" # include "dex-version-macros.h" #undef DEX_INSIDE G_END_DECLS 0707010000007A000081A40000000000000000000000016712D33C000015E3000000000000000000000000000000000000001D00000000libdex-0.8.1/src/meson.buildlibdex_sources = [ 'dex-aio.c', 'dex-aio-backend.c', 'dex-async-pair.c', 'dex-async-result.c', 'dex-block.c', 'dex-cancellable.c', 'dex-channel.c', 'dex-delayed.c', 'dex-enums.c', 'dex-error.c', 'dex-fiber.c', 'dex-future.c', 'dex-future-set.c', 'dex-gio.c', 'dex-init.c', 'dex-infinite.c', 'dex-main-scheduler.c', 'dex-object.c', 'dex-platform.c', 'dex-posix-aio-backend.c', 'dex-posix-aio-future.c', 'dex-promise.c', 'dex-scheduler.c', 'dex-semaphore.c', 'dex-stack.c', 'dex-static-future.c', 'dex-thread-pool-scheduler.c', 'dex-thread-pool-worker.c', 'dex-thread-storage.c', 'dex-timeout.c', 'dex-work-queue.c', 'dex-work-stealing-queue.c', ] libdex_headers = [ 'dex-aio.h', 'dex-async-pair.h', 'dex-async-result.h', 'dex-block.h', 'dex-cancellable.h', 'dex-channel.h', 'dex-delayed.h', 'dex-enums.h', 'dex-error.h', 'dex-fiber.h', 'dex-future.h', 'dex-future-set.h', 'dex-gio.h', 'dex-init.h', 'dex-main-scheduler.h', 'dex-object.h', 'dex-platform.h', 'dex-promise.h', 'dex-scheduler.h', 'dex-static-future.h', 'dex-thread-pool-scheduler.h', 'dex-timeout.h', 'dex-version-macros.h', 'libdex.h', ] libdex_deps = [ cc.find_library('atomic', required: false), glib_dep, ] if liburing_dep.found() libdex_sources += [ 'dex-uring-aio-backend.c', 'dex-uring-future.c', ] libdex_deps += [liburing_dep] liburing_version_split = liburing_dep.version().split('.') liburing_version_conf = configuration_data() liburing_version_conf.set('DEX_URING_VERSION', meson.project_version()) liburing_version_conf.set('DEX_URING_MAJOR_VERSION', liburing_version_split[0]) liburing_version_conf.set('DEX_URING_MINOR_VERSION', liburing_version_split[1]) configure_file( input: 'dex-uring-version.h.in', output: 'dex-uring-version.h', configuration: liburing_version_conf, ) endif if host_machine.system() != 'windows' # Not really used on Windows now, see also # https://github.com/mesonbuild/meson/issues/4366 libdex_sources += [ 'dex-unix-signal.c', 'dex-ucontext.c', ] # If we're on Linux and mips we might still need asm.S. # But otherwise linux can do it all without any custom # assembler. Failure to do this might result in CET being # disabled for the process by GCC. if ((host_machine.system() != 'linux' or host_machine.cpu_family() == 'mips') and host_machine.system() != 'darwin') libdex_sources += ['asm.S'] endif libdex_headers += ['dex-unix-signal.h'] endif version_split = meson.project_version().split('.') version_conf = configuration_data() version_conf.set('VERSION', meson.project_version()) version_conf.set('MAJOR_VERSION', version_split[0]) version_conf.set('MINOR_VERSION', version_split[1]) version_conf.set('MICRO_VERSION', version_split[2]) configure_file( input: 'dex-version.h.in', output: 'dex-version.h', configuration: version_conf, install: true, install_dir: join_paths(get_option('includedir'), 'libdex-@0@'.format(api_version)) ) if get_option('sysprof') libdex_deps += [libsysprof_capture_dep] endif libdex_c_args = [ deprecated_c_args, release_args, '-DG_LOG_DOMAIN="Dex"', '-DDEX_COMPILATION', ] if host_machine.system() == 'darwin' libdex_c_args += [ # needed for setcontext/makecontext/swapcontext '-D_XOPEN_SOURCE', ] endif libdex_static = static_library('dex-@0@'.format(api_version), libdex_sources, dependencies: libdex_deps, gnu_symbol_visibility: 'hidden', c_args: libdex_c_args, ) libdex_static_dep = declare_dependency( link_whole: libdex_static, include_directories: include_directories('.'), dependencies: libdex_deps, ) libdex = shared_library('dex-@0@'.format(api_version), dependencies: libdex_deps + [libdex_static_dep], c_args: libdex_c_args, install: true, gnu_symbol_visibility: 'hidden', version: '@0@.0.0'.format(api_version), darwin_versions: '1.0', ) libdex_dep_sources = [] install_headers(libdex_headers, subdir: 'libdex-@0@'.format(api_version)) pkg.generate( description: 'Future-based programming for GLib-based applications and libraries', libraries: libdex, name: 'libdex', filebase: 'libdex-' + api_version, subdirs: 'libdex-@0@'.format(api_version), requires: ['gio-2.0'], ) if get_option('introspection').enabled() libdex_gir = gnome.generate_gir(libdex, sources: [libdex_sources, libdex_headers], nsversion: api_version, namespace: 'Dex', export_packages: 'libdex-@0@'.format(api_version), symbol_prefix: 'dex', identifier_prefix: 'Dex', install_dir_gir: girdir, install_dir_typelib: typelibdir, includes: ['Gio-2.0'], install: true, header: 'libdex.h', extra_args: ['-DDEX_COMPILATION'], ) libdex_dep_sources += [libdex_gir] if get_option('vapi') dex_vapi = gnome.generate_vapi('libdex-@0@'.format(api_version), sources: libdex_gir.get(0), install: true, install_dir: join_paths(datadir, 'vala', 'vapi'), packages: ['gio-2.0'], ) endif endif libdex_dep = declare_dependency( link_with: libdex, include_directories: include_directories('.'), dependencies: libdex_deps, sources: [libdex_dep_sources], ) meson.override_dependency('libdex-' + api_version, libdex_dep) 0707010000007B000081A40000000000000000000000016712D33C00000C8E000000000000000000000000000000000000002100000000libdex-0.8.1/src/mips-ucontext.htypedef struct mcontext mcontext_t; typedef struct ucontext ucontext_t; /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Ralph Campbell. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ucontext.h 8.1 (Berkeley) 6/10/93 * JNPR: ucontext.h,v 1.2 2007/08/09 11:23:32 katta * $FreeBSD: src/sys/mips/include/ucontext.h,v 1.2 2010/01/10 19:50:24 imp Exp $ */ struct mcontext { /* * These fields must match the corresponding fields in struct * sigcontext which follow 'sc_mask'. That way we can support * struct sigcontext and ucontext_t at the same time. */ int mc_onstack; /* sigstack state to restore */ int mc_pc; /* pc at time of signal */ int mc_regs[32]; /* processor regs 0 to 31 */ int sr; /* status register */ int mullo, mulhi; /* mullo and mulhi registers... */ int mc_fpused; /* fp has been used */ int mc_fpregs[33]; /* fp regs 0 to 31 and csr */ int mc_fpc_eir; /* fp exception instruction reg */ void *mc_tls; /* pointer to TLS area */ int __spare__[8]; /* reserved */ }; struct ucontext { /* * Keep the order of the first two fields. Also, * keep them the first two fields in the structure. * This way we can have a union with struct * sigcontext and ucontext_t. This allows us to * support them both at the same time. * note: the union is not defined, though. */ sigset_t uc_sigmask; mcontext_t uc_mcontext; struct __ucontext *uc_link; stack_t uc_stack; int uc_flags; int __spare__[4]; }; 0707010000007C000081A40000000000000000000000016712D33C000003AC000000000000000000000000000000000000002200000000libdex-0.8.1/src/power-ucontext.h#define setcontext(u) _setmcontext(&(u)->mc) #define getcontext(u) _getmcontext(&(u)->mc) typedef struct mcontext mcontext_t; typedef struct ucontext ucontext_t; struct mcontext { ulong pc; /* lr */ ulong cr; /* mfcr */ ulong ctr; /* mfcr */ ulong xer; /* mfcr */ ulong sp; /* callee saved: r1 */ ulong toc; /* callee saved: r2 */ ulong r3; /* first arg to function, return register: r3 */ ulong gpr[19]; /* callee saved: r13-r31 */ /* // currently do not save vector registers or floating-point state // ulong pad; // uvlong fpr[18]; / * callee saved: f14-f31 * / // ulong vr[4*12]; / * callee saved: v20-v31, 256-bits each * / */ }; struct ucontext { struct { void *ss_sp; uint ss_size; } uc_stack; sigset_t uc_sigmask; mcontext_t mc; }; void makecontext(ucontext_t*, void(*)(void), int, ...); int swapcontext(ucontext_t*, const ucontext_t*); int _getmcontext(mcontext_t*); void _setmcontext(const mcontext_t*); 0707010000007D000041ED0000000000000000000000026712D33C00000000000000000000000000000000000000000000001700000000libdex-0.8.1/testsuite0707010000007E000081A40000000000000000000000016712D33C00000547000000000000000000000000000000000000002100000000libdex-0.8.1/testsuite/glib.supp{ <boxed_type> Memcheck:Leak ... fun:g_boxed_type_register_static } { <quark_init> Memcheck:Leak ... fun:g_quark_init } { <quark_strdup> Memcheck:Leak ... fun:quark_strdup } { <quark_new> Memcheck:Leak ... fun:quark_new } { <type_reg> Memcheck:Leak ... fun:g_type_register_static } { <rand_init> Memcheck:Leak ... fun:g_rand_new } { <gobject_init> Memcheck:Leak ... fun:gobject_init } { <gsignl_init> Memcheck:Leak ... fun:_g_signal_init } { <gerror_init> Memcheck:Leak ... fun:g_error_init } { <gtest_init> Memcheck:Leak ... fun:g_test_init } { <test_run_seed> Memcheck:Leak ... fun:test_run_seed } { <test_paths> Memcheck:Leak ... fun:g_string_maybe_expand fun:g_string_sized_new fun:g_build_path_va fun:g_build_path fun:g_test_run_suite_internal } { <test_suite_add> Memcheck:Leak ... fun:g_test_add_vtable } { <test_suite_run> Memcheck:Leak ... fun:g_timer_new fun:test_suite_run } { <test_suite_run> Memcheck:Leak ... fun:g_strdup_printf fun:g_test_run_suite } { <gprivate> Memcheck:Leak ... fun:g_private_impl_new } { <thread_new> Memcheck:Leak ... fun:g_thread_new } { <sysprof_collector> Memcheck:Leak ... fun:sysprof_malloc0 } { <dex_init> Memcheck:Leak ... fun:dex_init_once } 0707010000007F000081A40000000000000000000000016712D33C0000039D000000000000000000000000000000000000002300000000libdex-0.8.1/testsuite/meson.buildtest_env = [ 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 'G_DEBUG=gc-friendly', 'GSETTINGS_BACKEND=memory', 'MALLOC_CHECK_=2', 'NO_AT_BRIDGE=1', ] testsuite_c_args = [ '-DDEX_COMPILATION', '-DG_LOG_DOMAIN="libdex"', '-DG_ENABLE_DEBUG', '-UG_DISABLE_ASSERT', '-UG_DISABLE_CAST_CHECKS', ] testsuite = { 'test-async-result': {}, 'test-channel': {}, 'test-object': {}, 'test-fiber': {}, 'test-future': {}, 'test-scheduler': {}, 'test-semaphore': {}, 'test-stream': {}, } testsuite_deps = [ libdex_static_dep, ] if get_option('sysprof') testsuite_deps += [libsysprof_capture_dep] endif foreach test, params: testsuite test_exe = executable(test, '@0@.c'.format(test), c_args: testsuite_c_args + deprecated_c_args, dependencies: testsuite_deps, ) test(test, test_exe, env: test_env) endforeach 07070100000080000081A40000000000000000000000016712D33C00000952000000000000000000000000000000000000002B00000000libdex-0.8.1/testsuite/test-async-result.c/* * test-async-result.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> typedef struct { GMainLoop *main_loop; GObject *object; GError *error; int value; } State; static void test_async_result_await_cb (GObject *object, GAsyncResult *result, gpointer user_data) { State *state = user_data; g_assert_true (G_IS_MENU (object)); g_assert_true (DEX_IS_ASYNC_RESULT (result)); g_assert_nonnull (state); g_assert_true (object == state->object); state->value = dex_async_result_propagate_int (DEX_ASYNC_RESULT (result), &state->error); g_main_loop_quit (state->main_loop); } static void test_async_result_await (void) { GMainLoop *main_loop = g_main_loop_new (NULL, FALSE); DexPromise *future = dex_promise_new (); GMenu *obj = g_menu_new (); State state = {0}; DexAsyncResult *async_result = dex_async_result_new (obj, NULL, test_async_result_await_cb, &state); state.main_loop = main_loop; state.object = G_OBJECT (obj); dex_async_result_await (async_result, dex_ref (future)); g_clear_object (&async_result); dex_promise_resolve_int (future, 123); g_main_loop_run (main_loop); g_assert_cmpint (state.value, ==, 123); g_object_unref (obj); dex_unref (future); while (g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); g_main_loop_unref (main_loop); } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/AsyncResult/await", test_async_result_await); return g_test_run (); } 07070100000081000081A40000000000000000000000016712D33C00001452000000000000000000000000000000000000002600000000libdex-0.8.1/testsuite/test-channel.c/* * test-channel.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> #define ASSERT_STATUS(f,status) g_assert_cmpint(status, ==, dex_future_get_status(DEX_FUTURE(f))) #define ASSERT_CMP(future, kind, get, op, v) \ G_STMT_START { \ GError *error = NULL; \ const GValue *value = dex_future_get_value (DEX_FUTURE (future), &error); \ g_assert_no_error (error); \ g_assert_nonnull (value); \ G_PASTE (g_assert_cmp, kind) (G_PASTE (g_value_get_, get) (value), op, v); \ } G_STMT_END #define ASSERT_CMPINT(future, op, value) ASSERT_CMP(future, int, int, op, value) #define ASSERT_CMPUINT(future, op, value) ASSERT_CMP(future, int, uint, op, value) static void test_channel_basic (void) { DexChannel *channel; DexFuture *value1 = NULL; DexFuture *value2 = NULL; DexFuture *value3 = NULL; DexFuture *send1 = NULL; DexFuture *send2 = NULL; DexFuture *send3 = NULL; DexFuture *recv1 = NULL; DexFuture *recv2 = NULL; DexFuture *recv3 = NULL; channel = dex_channel_new (2); g_assert_true (dex_channel_can_send (channel)); g_assert_true (dex_channel_can_receive (channel)); value1 = dex_future_new_for_int (1); value2 = dex_future_new_for_int (2); value3 = dex_future_new_for_int (3); ASSERT_CMPINT (value1, ==, 1); ASSERT_CMPINT (value2, ==, 2); ASSERT_CMPINT (value3, ==, 3); send1 = dex_channel_send (channel, dex_ref (value1)); g_assert_true ((gpointer)send1 != (gpointer)value1); g_assert_true (dex_channel_can_send (channel)); g_assert_true (dex_channel_can_receive (channel)); ASSERT_STATUS (send1, DEX_FUTURE_STATUS_RESOLVED); ASSERT_CMPUINT (send1, ==, 1); send2 = dex_channel_send (channel, dex_ref (value2)); g_assert_true (dex_channel_can_send (channel)); g_assert_true (dex_channel_can_receive (channel)); ASSERT_STATUS (send2, DEX_FUTURE_STATUS_RESOLVED); ASSERT_CMPUINT (send2, ==, 2); send3 = dex_channel_send (channel, dex_ref (value3)); g_assert_true (dex_channel_can_send (channel)); g_assert_true (dex_channel_can_receive (channel)); ASSERT_STATUS (send3, DEX_FUTURE_STATUS_PENDING); dex_channel_close_send (channel); g_assert_false (dex_channel_can_send (channel)); g_assert_true (dex_channel_can_receive (channel)); ASSERT_STATUS (send3, DEX_FUTURE_STATUS_PENDING); recv1 = dex_channel_receive (channel); ASSERT_STATUS (send3, DEX_FUTURE_STATUS_RESOLVED); ASSERT_STATUS (recv1, DEX_FUTURE_STATUS_RESOLVED); ASSERT_CMPUINT (send3, ==, 2); ASSERT_CMPINT (recv1, ==, 1); recv2 = dex_channel_receive (channel); ASSERT_STATUS (recv2, DEX_FUTURE_STATUS_RESOLVED); ASSERT_CMPINT (recv2, ==, 2); dex_channel_close_receive (channel); g_assert_false (dex_channel_can_send (channel)); g_assert_false (dex_channel_can_receive (channel)); recv3 = dex_channel_receive (channel); ASSERT_STATUS (recv3, DEX_FUTURE_STATUS_REJECTED); dex_clear (&value1); dex_clear (&value2); dex_clear (&value3); dex_clear (&send1); dex_clear (&send2); dex_clear (&send3); dex_clear (&recv1); dex_clear (&recv2); dex_clear (&recv3); dex_clear (&channel); } static void test_channel_recv_first (void) { DexChannel *channel = dex_channel_new (2); DexFuture *recv1 = dex_channel_receive (channel); DexFuture *recv2 = dex_channel_receive (channel); DexFuture *recv3 = dex_channel_receive (channel); DexFuture *recv4; DexFuture *value1 = dex_future_new_for_int (123); DexFuture *send1; ASSERT_STATUS (recv1, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (recv2, DEX_FUTURE_STATUS_PENDING); send1 = dex_channel_send (channel, dex_ref (value1)); ASSERT_STATUS (send1, DEX_FUTURE_STATUS_RESOLVED); ASSERT_STATUS (recv1, DEX_FUTURE_STATUS_RESOLVED); ASSERT_STATUS (recv2, DEX_FUTURE_STATUS_PENDING); dex_channel_close_send (channel); ASSERT_STATUS (recv2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (recv3, DEX_FUTURE_STATUS_REJECTED); recv4 = dex_channel_receive (channel); ASSERT_STATUS (recv4, DEX_FUTURE_STATUS_REJECTED); dex_clear (&channel); dex_clear (&recv1); dex_clear (&recv2); dex_clear (&recv3); dex_clear (&recv4); dex_clear (&value1); dex_clear (&send1); } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/Channel/basic", test_channel_basic); g_test_add_func ("/Dex/TestSuite/Channel/recv_first", test_channel_recv_first); return g_test_run (); } 07070100000082000081A40000000000000000000000016712D33C00001445000000000000000000000000000000000000002400000000libdex-0.8.1/testsuite/test-fiber.c/* test-fiber.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> #include "dex-fiber-private.h" #define ASSERT_STATUS(f,status) g_assert_cmpint(status, ==, dex_future_get_status(DEX_FUTURE(f))) #define ASSERT_ERROR(f,d,c) \ G_STMT_START { \ GError *_error = NULL; \ ASSERT_STATUS (f, DEX_FUTURE_STATUS_REJECTED); \ g_assert_null (dex_future_get_value (DEX_FUTURE (f), &_error)); \ g_assert_cmpint (_error->domain, ==, d); \ g_assert_cmpint (_error->code, ==, c); \ g_clear_error (&_error); \ } G_STMT_END static int test_arg = 123; static DexFuture * scheduler_fiber_func (gpointer user_data) { test_arg = 99; return dex_future_new_for_int (99); } static DexFuture * scheduler_fiber_error (gpointer user_data) { return NULL; } static void test_fiber_scheduler_basic (void) { DexFiberScheduler *fiber_scheduler = dex_fiber_scheduler_new (); DexFiber *fiber = dex_fiber_new (scheduler_fiber_func, NULL, NULL, 0); const GValue *value; g_source_attach ((GSource *)fiber_scheduler, NULL); test_arg = 0; dex_future_set_static_name (DEX_FUTURE (fiber), "fiber_func"); dex_fiber_scheduler_register (fiber_scheduler, fiber); while (g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); g_assert_cmpint (test_arg, ==, 99); ASSERT_STATUS (fiber, DEX_FUTURE_STATUS_RESOLVED); value = dex_future_get_value (DEX_FUTURE (fiber), NULL); g_assert_cmpint (99, ==, g_value_get_int (value)); dex_clear (&fiber); fiber = dex_fiber_new (scheduler_fiber_error, NULL, NULL, 0); dex_future_set_static_name (DEX_FUTURE (fiber), "fiber_error"); dex_fiber_scheduler_register (fiber_scheduler, fiber); while (g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); ASSERT_ERROR (fiber, DEX_ERROR, DEX_ERROR_FIBER_EXITED); dex_clear (&fiber); g_source_destroy ((GSource *)fiber_scheduler); g_source_unref ((GSource *)fiber_scheduler); } static DexFuture * test_await_send (gpointer user_data) { DexChannel *channel = user_data; for (guint i = 0; i < 4; i++) { DexFuture *send; const GValue *value; GError *error = NULL; send = dex_channel_send (channel, dex_future_new_for_int (200)); g_assert_true (dex_await (dex_ref (send), &error)); g_assert_no_error (error); value = dex_future_get_value (send, NULL); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_UINT (value)); dex_unref (send); } return dex_future_new_for_boolean (TRUE); } static DexFuture * test_await_recv (gpointer user_data) { DexChannel *channel = user_data; for (guint i = 0; i < 4; i++) { DexFuture *recv; GError *error = NULL; const GValue *value; recv = dex_channel_receive (channel); g_assert_true (dex_await (dex_ref (recv), &error)); g_assert_no_error (error); value = dex_future_get_value (recv, NULL); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (200, ==, g_value_get_int (value)); dex_unref (recv); } return dex_future_new_for_boolean (TRUE); } static void test_fiber_scheduler_await (void) { DexFiberScheduler *fiber_scheduler = dex_fiber_scheduler_new (); DexChannel *channel = dex_channel_new (0); DexFiber *fiber1 = dex_fiber_new (test_await_send, dex_ref (channel), dex_unref, 0); DexFiber *fiber2 = dex_fiber_new (test_await_recv, dex_ref (channel), dex_unref, 0); dex_future_set_static_name (DEX_FUTURE (fiber1), "fiber1"); dex_future_set_static_name (DEX_FUTURE (fiber2), "fiber2"); dex_fiber_scheduler_register (fiber_scheduler, fiber2); dex_fiber_scheduler_register (fiber_scheduler, fiber1); g_source_attach ((GSource *)fiber_scheduler, NULL); while (g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); ASSERT_STATUS (fiber1, DEX_FUTURE_STATUS_RESOLVED); ASSERT_STATUS (fiber2, DEX_FUTURE_STATUS_RESOLVED); dex_clear (&fiber1); dex_clear (&fiber2); dex_clear (&channel); g_source_destroy ((GSource *)fiber_scheduler); g_source_unref ((GSource *)fiber_scheduler); } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/FiberScheduler/basic", test_fiber_scheduler_basic); g_test_add_func ("/Dex/TestSuite/FiberScheduler/await", test_fiber_scheduler_await); return g_test_run (); } 07070100000083000081A40000000000000000000000016712D33C000084FD000000000000000000000000000000000000002500000000libdex-0.8.1/testsuite/test-future.c/* test-future.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libdex.h> #include <gio/gio.h> #include "dex-future-private.h" #include "dex-async-pair-private.h" #define ASSERT_STATUS(f,status) g_assert_cmpint(status, ==, dex_future_get_status(DEX_FUTURE(f))) #define ASSERT_INSTANCE_TYPE(obj,type) \ G_STMT_START { \ if (!G_TYPE_CHECK_INSTANCE_TYPE((obj), (type))) \ g_error ("%s: %s is not a %s", \ G_STRLOC, \ (obj) ? G_OBJECT_TYPE_NAME((obj)) \ : "NULL", g_type_name(type)); \ } G_STMT_END typedef struct { guint catch; guint destroy; guint finally; guint then; } TestInfo; static DexFuture * catch_cb (DexFuture *future, gpointer user_data) { TestInfo *info = user_data; g_atomic_int_inc (&info->catch); return DEX_FUTURE (dex_future_new_for_string ("123")); } static DexFuture * then_cb (DexFuture *future, gpointer user_data) { TestInfo *info = user_data; const GValue *value; GError *error = NULL; g_atomic_int_inc (&info->then); value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_STRING (value)); g_assert_cmpstr (g_value_get_string (value), ==, "123"); return DEX_FUTURE (dex_future_new_for_int (123)); } static DexFuture * finally_cb (DexFuture *future, gpointer user_data) { TestInfo *info = user_data; const GValue *value; GError *error = NULL; g_atomic_int_inc (&info->finally); value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 123); return NULL; } static void destroy_cb (gpointer data) { TestInfo *info = data; g_atomic_int_inc (&info->destroy); } static void test_future_then (void) { DexCancellable *cancellable; const GValue *resolved; DexFuture *future; GError *error = NULL; TestInfo info = {0}; cancellable = dex_cancellable_new (); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (cancellable)), ==, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancellable); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (cancellable)), ==, DEX_FUTURE_STATUS_REJECTED); future = dex_future_catch (dex_ref (cancellable), catch_cb, &info, destroy_cb); g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_RESOLVED); future = dex_future_then (future, then_cb, &info, destroy_cb); g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_RESOLVED); future = dex_future_finally (future, finally_cb, &info, destroy_cb); g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_RESOLVED); resolved = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (resolved); g_assert_true (G_VALUE_HOLDS_INT (resolved)); g_assert_cmpint (g_value_get_int (resolved), ==, 123); dex_unref (future); dex_unref (cancellable); g_assert_cmpint (info.catch, ==, 1); g_assert_cmpint (info.finally, ==, 1); g_assert_cmpint (info.then, ==, 1); g_assert_cmpint (info.destroy, ==, 3); } static void test_cancellable_cancel (void) { DexCancellable *future = dex_cancellable_new (); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (future)), ==, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (future); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (future)), ==, DEX_FUTURE_STATUS_REJECTED); dex_unref (future); } static DexFuture * on_timed_out (DexFuture *future, gpointer user_data) { g_main_loop_quit (user_data); return NULL; } static void test_timeout (void) { GMainLoop *main_loop = g_main_loop_new (NULL, FALSE); DexFuture *timeout = dex_timeout_new_deadline (g_get_monotonic_time ()); DexFuture *future = dex_future_catch (dex_ref (timeout), on_timed_out, main_loop, NULL); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (timeout)), ==, DEX_FUTURE_STATUS_PENDING); g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_PENDING); g_main_loop_run (main_loop); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (timeout)), ==, DEX_FUTURE_STATUS_REJECTED); g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_REJECTED); dex_clear (&future); dex_clear (&timeout); g_main_loop_unref (main_loop); } static void test_promise_type (void) { g_assert_true (dex_promise_get_type() != G_TYPE_INVALID); g_assert_true (G_TYPE_IS_INSTANTIATABLE (dex_promise_get_type())); g_assert_false (G_TYPE_IS_ABSTRACT (dex_promise_get_type())); g_assert_true (G_TYPE_IS_FINAL (dex_promise_get_type())); } static void test_promise_autoptr (void) { G_GNUC_UNUSED g_autoptr(DexPromise) promise = NULL; } static void test_promise_new (void) { DexPromise *promise; promise = dex_promise_new (); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (promise)), ==, DEX_FUTURE_STATUS_PENDING); g_assert_nonnull (promise); g_assert_true (DEX_IS_FUTURE (promise)); dex_clear (&promise); } static void test_static_future_new (void) { DexFuture *future; future = dex_future_new_for_string ("123"); g_assert_true (DEX_IS_STATIC_FUTURE (future)); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (future)), ==, DEX_FUTURE_STATUS_RESOLVED); dex_clear (&future); future = dex_future_new_for_int (123); g_assert_true (DEX_IS_STATIC_FUTURE (future)); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (future)), ==, DEX_FUTURE_STATUS_RESOLVED); dex_clear (&future); future = dex_future_new_for_boolean (TRUE); g_assert_true (DEX_IS_STATIC_FUTURE (future)); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (future)), ==, DEX_FUTURE_STATUS_RESOLVED); dex_clear (&future); future = dex_future_new_for_error (g_error_new_literal (G_IO_ERROR, G_IO_ERROR_PENDING, "pending")); g_assert_true (DEX_IS_STATIC_FUTURE (future)); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (future)), ==, DEX_FUTURE_STATUS_REJECTED); dex_clear (&future); } static void test_promise_resolve (void) { DexPromise *promise = dex_promise_new (); GValue value = G_VALUE_INIT; const GValue *resolved; GError *error = NULL; g_assert_cmpint (dex_future_get_status (DEX_FUTURE (promise)), ==, DEX_FUTURE_STATUS_PENDING); g_value_init (&value, G_TYPE_BOOLEAN); g_value_set_boolean (&value, TRUE); dex_promise_resolve (promise, &value); g_assert_cmpint (dex_future_get_status (DEX_FUTURE (promise)), ==, DEX_FUTURE_STATUS_RESOLVED); resolved = dex_future_get_value (DEX_FUTURE (promise), &error); g_assert_no_error (error); g_assert_cmpint (G_VALUE_TYPE (resolved), ==, G_VALUE_TYPE (&value)); g_assert_cmpint (g_value_get_boolean (resolved), ==, g_value_get_boolean (&value)); g_value_unset (&value); dex_unref (promise); } #define ASYNC_TEST(T, TYPE, NAME, propagate, gvalue, res, cmp) \ typedef struct G_PASTE (_Test, T) G_PASTE (Test, T); \ static void \ G_PASTE (test_async_, T) (G_PASTE (Test, T) *instance, \ GCancellable *cancellable, \ GAsyncReadyCallback callback, \ gpointer user_data) \ { \ GTask *task = g_task_new (instance, cancellable, callback, user_data); \ g_assert_true (G_IS_OBJECT (instance)); \ g_assert_true (!cancellable || G_IS_CANCELLABLE (cancellable)); \ g_assert_true (callback != NULL); \ G_PASTE (g_task_return_, propagate)(task, res); \ g_object_unref (task); \ } \ static T \ G_PASTE (test_finish_, T)(G_PASTE (Test, T) *instance, GAsyncResult *result, GError **error) \ { \ g_assert_true (G_IS_TASK (result)); \ g_assert_true (G_IS_OBJECT (instance)); \ return G_PASTE (g_task_propagate_, propagate) (G_TASK (result), error); \ } \ static DexFuture * \ G_PASTE (test_complete_, T) (DexFuture *future, gpointer user_data) \ { \ GMainLoop *main_loop = user_data; \ GError *error = NULL; \ const GValue *value = dex_future_get_value (future, &error); \ g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_RESOLVED); \ g_assert_no_error (error); \ g_assert_true (G_PASTE (G_VALUE_HOLDS_, NAME)(value)); \ G_PASTE (g_assert_, cmp) (G_PASTE (g_value_get_, gvalue) (value), ==, res); \ g_main_loop_quit (main_loop); \ return NULL; \ } \ static void \ G_PASTE (test_async_pair_, T) (void) \ { \ GMainLoop *main_loop = g_main_loop_new (NULL, FALSE); \ GObject *object = G_OBJECT (g_menu_new ()); \ DexFuture *future = dex_async_pair_new (object, \ &DEX_ASYNC_PAIR_INFO (G_PASTE (test_async_, T), G_PASTE (test_finish_, T), TYPE)); \ future = dex_future_finally (future, G_PASTE (test_complete_, T), main_loop, NULL); \ g_main_loop_run (main_loop); \ dex_unref (future); \ g_clear_object (&object); \ g_main_loop_unref (main_loop); \ } ASYNC_TEST (gboolean, G_TYPE_BOOLEAN, BOOLEAN, boolean, boolean, TRUE, cmpint) ASYNC_TEST (int, G_TYPE_INT, INT, int, int, 123, cmpint) ASYNC_TEST (guint, G_TYPE_UINT, UINT, int, uint, 321, cmpint) ASYNC_TEST (gint64, G_TYPE_INT64, INT64, int, int64, -123123123L, cmpint) ASYNC_TEST (guint64, G_TYPE_UINT64, UINT64, int, uint64, 123123123L, cmpint) ASYNC_TEST (glong, G_TYPE_LONG, LONG, int, long, -123123, cmpint) ASYNC_TEST (gulong, G_TYPE_ULONG, ULONG, int, ulong, 123123, cmpint) ASYNC_TEST (DexFutureStatus, DEX_TYPE_FUTURE_STATUS, ENUM, int, enum, DEX_FUTURE_STATUS_REJECTED, cmpint) ASYNC_TEST (GSubprocessFlags, G_TYPE_SUBPROCESS_FLAGS, FLAGS, int, flags, (G_SUBPROCESS_FLAGS_STDIN_PIPE|G_SUBPROCESS_FLAGS_STDOUT_PIPE), cmpint) #define ASYNC_TEST_PTR(T, NAME, propagate, gvalue, res, cmp, copyfunc, freefunc) \ typedef struct G_PASTE (_Test, T) G_PASTE (Test, T); \ static void \ G_PASTE (test_async_, T) (G_PASTE (Test, T) *instance, \ GCancellable *cancellable, \ GAsyncReadyCallback callback, \ gpointer user_data) \ { \ GTask *task = g_task_new (instance, cancellable, callback, user_data); \ g_assert_true (G_IS_OBJECT (instance)); \ g_assert_true (!cancellable || G_IS_CANCELLABLE (cancellable)); \ g_assert_true (callback != NULL); \ G_PASTE (g_task_return_, propagate)(task, copyfunc, (GDestroyNotify)freefunc); \ g_object_unref (task); \ } \ static T * \ G_PASTE (test_finish_, T)(G_PASTE (Test, T) *instance, GAsyncResult *result, GError **error) \ { \ g_assert_true (G_IS_TASK (result)); \ g_assert_true (G_IS_OBJECT (instance)); \ return G_PASTE (g_task_propagate_, propagate) (G_TASK (result), error); \ } \ static DexFuture * \ G_PASTE (test_complete_, T) (DexFuture *future, gpointer user_data) \ { \ gpointer *state = user_data; \ GMainLoop *main_loop = state[0]; \ G_GNUC_UNUSED gpointer instance = state[1]; \ GError *error = NULL; \ const GValue *value = dex_future_get_value (future, &error); \ const T *ret = G_PASTE (g_value_get_, gvalue) (value); \ g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_RESOLVED); \ g_assert_no_error (error); \ g_assert_true (G_PASTE (G_VALUE_HOLDS_, NAME)(value)); \ g_assert_true (cmp(ret,res)); \ g_main_loop_quit (main_loop); \ return NULL; \ } \ static void \ G_PASTE (test_async_pair_, T) (void) \ { \ GMainLoop *main_loop = g_main_loop_new (NULL, FALSE); \ GObject *instance = G_OBJECT (g_menu_new ()); \ gpointer state[2] = { main_loop, instance }; \ DexFuture *future = dex_async_pair_new (instance, \ &G_PASTE (DEX_ASYNC_PAIR_INFO_, NAME) (G_PASTE (test_async_, T), \ G_PASTE (test_finish_, T))); \ future = dex_future_finally (future, G_PASTE (test_complete_, T), state, NULL); \ g_main_loop_run (main_loop); \ dex_unref (future); \ g_clear_object (&instance); \ g_main_loop_unref (main_loop); \ } ASYNC_TEST_PTR (char, STRING, pointer, string, "string-test", g_str_equal, g_strdup("string-test"), g_free) static void ptr_free (gpointer p) { } static gboolean cmpptr (gconstpointer a, gconstpointer b) { return a == b; } ASYNC_TEST_PTR (gpointer, POINTER, pointer, pointer, "a-pointer", cmpptr, (gpointer)"a-pointer", ptr_free) ASYNC_TEST_PTR (GObject, OBJECT, pointer, object, instance, cmpptr, g_object_ref(instance), g_object_unref) #define ASYNC_TEST_ERROR(T, NAME, propagate) \ typedef struct G_PASTE (_Test, T) G_PASTE (Test, T); \ static void \ G_PASTE (test_async_, T) (G_PASTE (Test, T) *instance, \ GCancellable *cancellable, \ GAsyncReadyCallback callback, \ gpointer user_data) \ { \ GTask *task = g_task_new (instance, cancellable, callback, user_data); \ g_assert_true (G_IS_OBJECT (instance)); \ g_assert_true (!cancellable || G_IS_CANCELLABLE (cancellable)); \ g_assert_true (callback != NULL); \ g_task_return_new_error (task, DEX_ERROR, DEX_ERROR_DEPENDENCY_FAILED, "Failed"); \ g_object_unref (task); \ } \ static T * \ G_PASTE (test_finish_, T)(G_PASTE (Test, T) *instance, GAsyncResult *result, GError **error) \ { \ g_assert_true (G_IS_TASK (result)); \ g_assert_true (G_IS_OBJECT (instance)); \ return G_PASTE (g_task_propagate_, propagate) (G_TASK (result), error); \ } \ static DexFuture * \ G_PASTE (test_complete_, T) (DexFuture *future, gpointer user_data) \ { \ GMainLoop *main_loop = user_data; \ GError *error = NULL; \ const GValue *value = dex_future_get_value (future, &error); \ g_assert_cmpint (dex_future_get_status (future), ==, DEX_FUTURE_STATUS_REJECTED); \ g_assert_error (error, DEX_ERROR, DEX_ERROR_DEPENDENCY_FAILED); \ g_assert_null (value); \ g_main_loop_quit (main_loop); \ return NULL; \ } \ static void \ G_PASTE (test_async_pair_, T) (void) \ { \ GMainLoop *main_loop = g_main_loop_new (NULL, FALSE); \ GObject *instance = G_OBJECT (g_menu_new ()); \ DexFuture *future = dex_async_pair_new (instance, \ &G_PASTE (DEX_ASYNC_PAIR_INFO_, NAME) (G_PASTE (test_async_, T), \ G_PASTE (test_finish_, T))); \ future = dex_future_finally (future, G_PASTE (test_complete_, T), main_loop, NULL); \ g_main_loop_run (main_loop); \ dex_unref (future); \ g_clear_object (&instance); \ g_main_loop_unref (main_loop); \ } typedef GObject ErrorTest; ASYNC_TEST_ERROR (ErrorTest, OBJECT, pointer) static void test_future_set_first_preresolved (void) { DexFuture *promise1 = dex_future_new_for_int (123); DexFuture *promise2 = dex_future_new_for_int (321); DexFuture *future = dex_future_first (DEX_FUTURE (promise1), promise2, NULL); GError *error = NULL; const GValue *value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 123); dex_unref (future); } static void test_future_set_all_race_preresolved (void) { DexFuture *promise1 = dex_future_new_for_int (123); DexFuture *promise2 = dex_future_new_for_int (321); DexFuture *future = dex_future_all_race (DEX_FUTURE (promise1), promise2, NULL); GError *error = NULL; const GValue *value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_BOOLEAN (value)); g_assert_cmpint (g_value_get_boolean (value), ==, TRUE); dex_unref (future); } static void test_future_set_any_preresolved (void) { DexFuture *promise1 = dex_future_new_for_int (123); DexFuture *promise2 = dex_future_new_for_int (321); DexFuture *future = dex_future_any (DEX_FUTURE (promise1), promise2, NULL); GError *error = NULL; const GValue *value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 123); dex_unref (future); } static void test_future_set_all_preresolved (void) { DexFuture *promise1 = dex_future_new_for_int (123); DexFuture *promise2 = dex_future_new_for_int (321); DexFuture *future = dex_future_all (DEX_FUTURE (promise1), promise2, NULL); GError *error = NULL; const GValue *value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_BOOLEAN (value)); g_assert_cmpint (g_value_get_boolean (value), ==, TRUE); dex_unref (future); } static void test_future_set_all_preresolved_error (void) { DexFuture *promise1 = dex_future_new_for_int (123); DexCancellable *cancel1 = dex_cancellable_new (); DexFuture *future; GError *error = NULL; const GValue *value; dex_cancellable_cancel (cancel1); future = dex_future_all (DEX_FUTURE (promise1), cancel1, NULL); value = dex_future_get_value (future, &error); g_assert_null (value); g_assert_error (error, DEX_ERROR, DEX_ERROR_DEPENDENCY_FAILED); g_clear_error (&error); dex_unref (future); } static void test_future_set_any_preresolved_error (void) { DexPromise *promise1 = dex_promise_new (); DexCancellable *cancel1 = dex_cancellable_new (); DexFuture *future; GError *error = NULL; const GValue *value; dex_cancellable_cancel (cancel1); future = dex_future_any (DEX_FUTURE (promise1), cancel1, NULL); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (promise1, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_promise_resolve_int (promise1, 123); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (promise1, DEX_FUTURE_STATUS_RESOLVED); ASSERT_STATUS (future, DEX_FUTURE_STATUS_RESOLVED); value = dex_future_get_value (future, &error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 123); g_clear_error (&error); dex_unref (future); } static void test_future_all (void) { DexCancellable *cancel1 = dex_cancellable_new (); DexCancellable *cancel2 = dex_cancellable_new (); DexCancellable *cancel3 = dex_cancellable_new (); const GValue *value; DexFuture *future; GError *error = NULL; future = dex_future_all (dex_ref (cancel1), dex_ref (cancel2), dex_ref (cancel3), NULL); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel1); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel2); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel3); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); value = dex_future_get_value (future, &error); g_assert_null (value); g_assert_error (error, DEX_ERROR, DEX_ERROR_DEPENDENCY_FAILED); g_clear_error (&error); g_assert_true (DEX_IS_FUTURE_SET (future)); g_assert_cmpint (dex_future_set_get_size (DEX_FUTURE_SET (future)), ==, 3); for (guint i = 0; i < dex_future_set_get_size (DEX_FUTURE_SET (future)); i++) { DexFuture *dep = dex_future_set_get_future_at (DEX_FUTURE_SET (future), i); value = dex_future_get_value (dep, &error); g_assert_null (value); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); } dex_clear (&cancel1); dex_clear (&cancel2); dex_clear (&cancel3); dex_clear (&future); } static void test_future_all_race (void) { DexCancellable *cancel1 = dex_cancellable_new (); DexCancellable *cancel2 = dex_cancellable_new (); DexCancellable *cancel3 = dex_cancellable_new (); const GValue *value; DexFuture *future; GError *error = NULL; future = dex_future_all_race (dex_ref (cancel1), dex_ref (cancel2), dex_ref (cancel3), NULL); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel1); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); dex_cancellable_cancel (cancel2); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); dex_cancellable_cancel (cancel3); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); value = dex_future_get_value (future, &error); g_assert_null (value); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); g_assert_true (DEX_IS_FUTURE_SET (future)); g_assert_cmpint (dex_future_set_get_size (DEX_FUTURE_SET (future)), ==, 3); for (guint i = 0; i < dex_future_set_get_size (DEX_FUTURE_SET (future)); i++) { DexFuture *dep = dex_future_set_get_future_at (DEX_FUTURE_SET (future), i); value = dex_future_get_value (dep, &error); g_assert_null (value); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); } dex_clear (&cancel1); dex_clear (&cancel2); dex_clear (&cancel3); dex_clear (&future); } static void test_future_any (void) { DexCancellable *cancel1 = dex_cancellable_new (); DexCancellable *cancel2 = dex_cancellable_new (); DexCancellable *cancel3 = dex_cancellable_new (); const GValue *value; DexFuture *future; GError *error = NULL; future = dex_future_any (dex_ref (cancel1), dex_ref (cancel2), dex_ref (cancel3), NULL); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel1); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel2); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel3); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); value = dex_future_get_value (future, &error); g_assert_null (value); g_assert_error (error, DEX_ERROR, DEX_ERROR_DEPENDENCY_FAILED); g_clear_error (&error); g_assert_true (DEX_IS_FUTURE_SET (future)); g_assert_cmpint (dex_future_set_get_size (DEX_FUTURE_SET (future)), ==, 3); for (guint i = 0; i < dex_future_set_get_size (DEX_FUTURE_SET (future)); i++) { DexFuture *dep = dex_future_set_get_future_at (DEX_FUTURE_SET (future), i); value = dex_future_get_value (dep, &error); g_assert_null (value); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); } dex_clear (&cancel1); dex_clear (&cancel2); dex_clear (&cancel3); dex_clear (&future); } static void test_future_first (void) { DexCancellable *cancel1 = dex_cancellable_new (); DexCancellable *cancel2 = dex_cancellable_new (); DexCancellable *cancel3 = dex_cancellable_new (); const GValue *value; DexFuture *future; GError *error = NULL; future = dex_future_first (dex_ref (cancel1), dex_ref (cancel2), dex_ref (cancel3), NULL); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_cancellable_cancel (cancel1); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); dex_cancellable_cancel (cancel2); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); dex_cancellable_cancel (cancel3); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel2, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (cancel3, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (future, DEX_FUTURE_STATUS_REJECTED); value = dex_future_get_value (future, &error); g_assert_null (value); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); g_assert_true (DEX_IS_FUTURE_SET (future)); g_assert_cmpint (dex_future_set_get_size (DEX_FUTURE_SET (future)), ==, 3); for (guint i = 0; i < dex_future_set_get_size (DEX_FUTURE_SET (future)); i++) { DexFuture *dep = dex_future_set_get_future_at (DEX_FUTURE_SET (future), i); value = dex_future_get_value (dep, &error); g_assert_null (value); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); } dex_clear (&cancel1); dex_clear (&cancel2); dex_clear (&cancel3); dex_clear (&future); } static void discard_async (GMenu *menu, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_print ("Discard async\n"); ASSERT_INSTANCE_TYPE (menu, G_TYPE_MENU); ASSERT_INSTANCE_TYPE (cancellable, G_TYPE_CANCELLABLE); task = g_task_new (menu, cancellable, callback, user_data); g_task_set_check_cancellable (task, TRUE); g_task_set_return_on_cancel (task, TRUE); } static gboolean discard_finish (GMenu *menu, GAsyncResult *result, GError **error) { GTask *task = G_TASK (result); gboolean ret; ASSERT_INSTANCE_TYPE (menu, G_TYPE_MENU); g_task_return_error_if_cancelled (task); ret = g_task_propagate_boolean (task, error); g_object_unref (task); return ret; } static void test_future_discard_cancelled (GCancellable *cancellable, gpointer user_data) { gboolean *was_cancelled = user_data; *was_cancelled = TRUE; } static void test_future_discard (void) { GMenu *menu = g_menu_new (); DexFuture *call = dex_async_pair_new (menu, &DEX_ASYNC_PAIR_INFO (discard_async, discard_finish, G_TYPE_BOOLEAN)); DexCancellable *cancel1; DexFuture *any; gboolean was_cancelled = FALSE; ASSERT_INSTANCE_TYPE (call, DEX_TYPE_ASYNC_PAIR); ASSERT_INSTANCE_TYPE (DEX_ASYNC_PAIR (call)->instance, G_TYPE_MENU); ASSERT_INSTANCE_TYPE (DEX_ASYNC_PAIR (call)->cancellable, G_TYPE_CANCELLABLE); cancel1 = dex_cancellable_new (); ASSERT_INSTANCE_TYPE (cancel1, DEX_TYPE_CANCELLABLE); any = dex_future_first (call, cancel1, NULL); ASSERT_INSTANCE_TYPE (any, DEX_TYPE_FUTURE_SET); g_signal_connect (DEX_ASYNC_PAIR (call)->cancellable, "cancelled", G_CALLBACK (test_future_discard_cancelled), &was_cancelled); dex_cancellable_cancel (cancel1); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (call, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (any, DEX_FUTURE_STATUS_REJECTED); g_assert_true (was_cancelled); ASSERT_STATUS (cancel1, DEX_FUTURE_STATUS_REJECTED); ASSERT_STATUS (call, DEX_FUTURE_STATUS_PENDING); ASSERT_STATUS (any, DEX_FUTURE_STATUS_REJECTED); g_clear_object (&menu); dex_clear (&any); } #ifdef G_OS_UNIX static void test_unix_signal_sigusr2 (void) { DexFuture *future = dex_unix_signal_new (SIGUSR2); const GValue *value; GError *error = NULL; kill (getpid (), SIGUSR2); while (dex_future_get_status (future) == DEX_FUTURE_STATUS_PENDING) g_main_context_iteration (g_main_context_default (), TRUE); ASSERT_STATUS (future, DEX_FUTURE_STATUS_RESOLVED); value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (SIGUSR2, ==, g_value_get_int (value)); dex_unref (future); } #endif static void test_delayed_simple (void) { DexFuture *result = dex_future_new_for_int (123); DexFuture *delayed = dex_delayed_new (dex_ref (result)); ASSERT_STATUS (result, DEX_FUTURE_STATUS_RESOLVED); ASSERT_STATUS (delayed, DEX_FUTURE_STATUS_PENDING); dex_delayed_release (DEX_DELAYED (delayed)); ASSERT_STATUS (delayed, DEX_FUTURE_STATUS_RESOLVED); dex_clear (&delayed); dex_clear (&result); } static void test_future_name (void) { DexFuture *future = DEX_FUTURE (dex_promise_new ()); dex_future_set_static_name (future, "futuristic programming"); g_assert_cmpstr ("futuristic programming", ==, dex_future_get_name (future)); dex_unref (future); } static void test_infinite_simple (void) { DexFuture *future = dex_future_new_infinite (); ASSERT_STATUS (future, DEX_FUTURE_STATUS_PENDING); dex_unref (future); } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/Future/name", test_future_name); g_test_add_func ("/Dex/TestSuite/Block/then", test_future_then); g_test_add_func ("/Dex/TestSuite/Cancellable/cancel", test_cancellable_cancel); g_test_add_func ("/Dex/TestSuite/StaticFuture/new", test_static_future_new); g_test_add_func ("/Dex/TestSuite/Promise/type", test_promise_type); g_test_add_func ("/Dex/TestSuite/Promise/autoptr", test_promise_autoptr); g_test_add_func ("/Dex/TestSuite/Promise/new", test_promise_new); g_test_add_func ("/Dex/TestSuite/Promise/resolve", test_promise_resolve); g_test_add_func ("/Dex/TestSuite/Timeout/timed-out", test_timeout); g_test_add_func ("/Dex/TestSuite/AsyncPair/boolean", test_async_pair_gboolean); g_test_add_func ("/Dex/TestSuite/AsyncPair/int", test_async_pair_int); g_test_add_func ("/Dex/TestSuite/AsyncPair/uint", test_async_pair_guint); g_test_add_func ("/Dex/TestSuite/AsyncPair/long", test_async_pair_glong); g_test_add_func ("/Dex/TestSuite/AsyncPair/ulong", test_async_pair_gulong); g_test_add_func ("/Dex/TestSuite/AsyncPair/int64", test_async_pair_gint64); g_test_add_func ("/Dex/TestSuite/AsyncPair/uint64", test_async_pair_guint64); g_test_add_func ("/Dex/TestSuite/AsyncPair/string", test_async_pair_char); g_test_add_func ("/Dex/TestSuite/AsyncPair/object", test_async_pair_GObject); g_test_add_func ("/Dex/TestSuite/AsyncPair/pointer", test_async_pair_gpointer); g_test_add_func ("/Dex/TestSuite/AsyncPair/flags", test_async_pair_GSubprocessFlags); g_test_add_func ("/Dex/TestSuite/AsyncPair/enums", test_async_pair_DexFutureStatus); g_test_add_func ("/Dex/TestSuite/AsyncPair/GError", test_async_pair_ErrorTest); g_test_add_func ("/Dex/TestSuite/Future/first_preresolved", test_future_set_first_preresolved); g_test_add_func ("/Dex/TestSuite/Future/all_race_preresolved", test_future_set_all_race_preresolved); g_test_add_func ("/Dex/TestSuite/Future/any_preresolved", test_future_set_any_preresolved); g_test_add_func ("/Dex/TestSuite/Future/all_preresolved", test_future_set_all_preresolved); g_test_add_func ("/Dex/TestSuite/Future/all_preresolved_error", test_future_set_all_preresolved_error); g_test_add_func ("/Dex/TestSuite/Future/any_preresolved_error", test_future_set_any_preresolved_error); g_test_add_func ("/Dex/TestSuite/Future/all", test_future_all); g_test_add_func ("/Dex/TestSuite/Future/all_race", test_future_all_race); g_test_add_func ("/Dex/TestSuite/Future/any", test_future_any); g_test_add_func ("/Dex/TestSuite/Future/first", test_future_first); g_test_add_func ("/Dex/TestSuite/Future/discard", test_future_discard); g_test_add_func ("/Dex/TestSuite/Delayed/simple", test_delayed_simple); g_test_add_func ("/Dex/TestSuite/Infinite/simple", test_infinite_simple); #ifdef G_OS_UNIX g_test_add_func ("/Dex/TestSuite/UnixSignal/sigusr2", test_unix_signal_sigusr2); #endif return g_test_run (); } 07070100000084000081A40000000000000000000000016712D33C00002FF3000000000000000000000000000000000000002500000000libdex-0.8.1/testsuite/test-object.c/* test-object.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <stdatomic.h> #include <libdex.h> #include "dex-object-private.h" static guint finalize_count; #define TEST_TYPE_OBJECT (test_object_get_type()) #define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, TEST_TYPE_OBJECT)) #define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, TEST_TYPE_OBJECT, TestObject)) GType test_object_get_type (void) G_GNUC_CONST; typedef struct _TestObject { DexObject parent_instance; int field1; guint field2; double field3; const char *field4; } TestObject; typedef struct _TestObjectClass { DexObjectClass parent_class; } TestObjectClass; DEX_DEFINE_FINAL_TYPE (TestObject, test_object, DEX_TYPE_OBJECT) static void test_object_finalize (DexObject *object) { g_atomic_int_inc (&finalize_count); DEX_OBJECT_CLASS (test_object_parent_class)->finalize (object); } static void test_object_class_init (TestObjectClass *test_object_class) { DexObjectClass *object_class = DEX_OBJECT_CLASS (test_object_class); object_class->finalize = test_object_finalize; } static void test_object_init (TestObject *test_object) { } static TestObject * test_object_new (void) { return (TestObject *)g_type_create_instance (TEST_TYPE_OBJECT); } static void test_weak_ref_st (void) { TestObject *to, *to2; DexWeakRef wr; DexWeakRef wr2; DexWeakRef wr3; finalize_count = 0; dex_weak_ref_init (&wr, NULL); g_assert_null (dex_weak_ref_get (&wr)); to = test_object_new (); dex_weak_ref_set (&wr, to); dex_ref (to); dex_unref (to); /* Add second weak-ref to test removing */ dex_weak_ref_init (&wr2, to); dex_weak_ref_set (&wr2, to); /* And a third weak-ref to test set API */ dex_weak_ref_init (&wr3, NULL); dex_weak_ref_set (&wr3, to); dex_weak_ref_set (&wr3, NULL); dex_weak_ref_set (&wr3, to); dex_weak_ref_clear (&wr2); dex_weak_ref_clear (&wr3); to2 = dex_weak_ref_get (&wr); g_assert_true (to == to2); dex_clear (&to2); dex_clear (&to); g_assert_cmpint (finalize_count, ==, 1); g_assert_null (dex_weak_ref_get (&wr)); dex_weak_ref_clear (&wr); } typedef struct { TestObject *to; DexWeakRef wr; GMutex wait1; GMutex wait2; GCond cond1; GCond cond2; } TestDexWeakRefMt; static gpointer test_weak_ref_mt_worker (gpointer data) { TestDexWeakRefMt *state = data; TestObject *to; g_assert_nonnull (state); g_assert_nonnull (state->to); /* Test that we can get a full ref from the weak, as we * should be certain to is still alive. */ g_mutex_lock (&state->wait1); to = dex_weak_ref_get (&state->wr); g_assert_nonnull (to); g_clear_pointer (&to, dex_unref); g_cond_signal (&state->cond1); g_mutex_unlock (&state->wait1); /* Acquire the second lock (which is held by thread-1 until * we signaled above, at which point it's waiting for a * second condition to signal. The first thread should have * dropped the final ref, and therefore our attempt to get * a ref from the weak ref should fail. */ g_mutex_lock (&state->wait2); to = dex_weak_ref_get (&state->wr); g_assert_null (to); g_cond_signal (&state->cond2); g_mutex_unlock (&state->wait2); return NULL; } static void test_weak_ref_mt (void) { TestDexWeakRefMt state; GThread *thread; finalize_count = 0; state.to = test_object_new (); dex_weak_ref_init (&state.wr, state.to); g_mutex_init (&state.wait1); g_mutex_init (&state.wait2); g_cond_init (&state.cond1); g_cond_init (&state.cond2); g_mutex_lock (&state.wait1); g_mutex_lock (&state.wait2); thread = g_thread_new ("test_weak_ref_mt_worker", test_weak_ref_mt_worker, &state); /* Now wait for thread-2 to signal us */ g_cond_wait (&state.cond1, &state.wait1); /* We were signaled, now clear our ref, then allow thread-2 * to acquire wait2 while we wait again to be signaled. */ g_clear_pointer (&state.to, dex_unref); g_cond_wait (&state.cond2, &state.wait2); g_mutex_unlock (&state.wait1); g_mutex_unlock (&state.wait2); g_thread_join (thread); dex_weak_ref_clear (&state.wr); g_mutex_clear (&state.wait1); g_mutex_clear (&state.wait2); g_cond_clear (&state.cond1); g_cond_clear (&state.cond2); } typedef struct { TestObject *to; DexWeakRef wr; GMutex mutex; GCond cond; } TestDexWeakRefExtendLiveness; static gpointer test_weak_ref_extend_liveness_worker (gpointer data) { TestDexWeakRefExtendLiveness *state = data; /* Prelock the weak-ref mutex so that we can make the * first thread block on acquiring it after it signals * our thread to continue. */ g_mutex_lock (&state->wr.mutex); g_assert (state->to != NULL); /* Now wait for thread-1 to release the mutex (while waiting * for our signal) and we can signal it to continue. */ g_mutex_lock (&state->mutex); g_cond_signal (&state->cond); g_mutex_unlock (&state->mutex); /* At this point, it is likely that we will get the first * thread to block on state->wr.mutex, but since we don't have * a condition to be signaled by, we just have to ... um, wait * and hope our CPU isn't running on hamster wheels. */ while (DEX_OBJECT (state->to)->ref_count > 0) g_usleep (G_USEC_PER_SEC / 1000); g_assert_cmpint (finalize_count, ==, 0); /* We still have state->wr.mutex, so pretend we've extended * liveness while it's attempting to finalize. */ g_atomic_int_inc (&DEX_OBJECT (state->to)->weak_refs_watermark); atomic_fetch_add_explicit (&DEX_OBJECT (state->to)->ref_count, 1, memory_order_relaxed); /* Now release our mutex so we can race against the * main thread to do the final unref. */ g_mutex_unlock (&state->wr.mutex); dex_unref (state->to); return NULL; } static void test_weak_ref_extend_liveness (void) { TestDexWeakRefExtendLiveness state; GThread *thread; finalize_count = 0; state.to = test_object_new (); dex_weak_ref_init (&state.wr, state.to); g_mutex_init (&state.mutex); g_cond_init (&state.cond); g_mutex_lock (&state.mutex); thread = g_thread_new ("test_weak_ref_extend_liveness_worker", test_weak_ref_extend_liveness_worker, &state); g_cond_wait (&state.cond, &state.mutex); dex_unref (state.to); g_mutex_unlock (&state.mutex); g_thread_join (thread); state.to = NULL; dex_weak_ref_clear (&state.wr); g_mutex_clear (&state.mutex); g_cond_clear (&state.cond); g_assert_cmpint (finalize_count, ==, 1); } typedef struct { TestObject *to; DexWeakRef wr; GMutex mutex; GCond cond; } TestDexWeakRefImmortal; static gpointer test_weak_ref_immortal_worker (gpointer data) { TestDexWeakRefImmortal *state = data; DexObject *borrowed; TestObject *to; borrowed = DEX_OBJECT (state->to); g_mutex_lock (&state->mutex); /* Acquire weak ref lock to force main thread to block */ dex_object_lock (borrowed); /* Now signal other thread to continue */ g_cond_signal (&state->cond); g_mutex_unlock (&state->mutex); /* Now wait until the ref count reaches zero, then bump the watermark * by creating a new reference. */ while (g_atomic_int_get (&borrowed->ref_count) > 0) g_usleep (G_USEC_PER_SEC / 1000); to = dex_weak_ref_get (&state->wr); g_assert_nonnull (to); /* UB here really, since it's overflow */ g_assert_cmpint (DEX_OBJECT (to)->weak_refs_watermark, ==, 0); /* Now release the lock allowing the main thread to continue */ dex_object_unlock (borrowed); g_mutex_lock (&state->mutex); dex_unref (to); g_mutex_unlock (&state->mutex); dex_weak_ref_clear (&state->wr); return NULL; } static void test_weak_ref_immortal (void) { TestDexWeakRefImmortal state; DexObject *borrowed; GThread *thread; finalize_count = 0; g_mutex_init (&state.mutex); g_cond_init (&state.cond); state.to = test_object_new (); borrowed = DEX_OBJECT (state.to); dex_weak_ref_init (&state.wr, state.to); /* Make object immortal */ borrowed->weak_refs_watermark = G_MAXUINT32; /* Now force a race on liveness from thread */ g_mutex_lock (&state.mutex); thread = g_thread_new ("test_weak_ref_immortal", test_weak_ref_immortal_worker, &state); g_cond_wait (&state.cond, &state.mutex); /* Try to unref forcing the race (blocking while acquiring the weak ref * mutex until the thread allows us to continue). */ dex_unref (state.to); g_assert_cmpint (finalize_count, ==, 0); /* Now release our lock allowing the thread to cleanup */ g_mutex_unlock (&state.mutex); g_thread_join (thread); g_mutex_clear (&state.mutex); g_cond_clear (&state.cond); /* Clear watermark and unref so we are valgrind clean. Consumers * can't actually do this, but that is the point. */ g_assert_cmpint (finalize_count, ==, 0); g_assert_cmpint (borrowed->ref_count, ==, 1); g_assert_cmpint (borrowed->weak_refs_watermark, ==, 1); borrowed->weak_refs_watermark = 1; borrowed->ref_count = 1; dex_unref (state.to); g_assert_cmpint (finalize_count, ==, 1); } static gboolean test_weak_ref_thread_guantlet_waiting; static gpointer test_weak_ref_thread_guantlet_worker (gpointer data) { DexWeakRef *wr = data; TestObject *to; guint i = 0; while ((to = dex_weak_ref_get (wr))) { dex_unref (to); g_usleep (G_USEC_PER_SEC / 10000 * g_random_int_range (0, 100)); /* It is possible, given a devilish enough of a thread scheduler, that * this could infinitely cause us to run due to always having a ref * greater than zero in one thread. * * So we read an atomic every now and again to break out. */ i++; if ((i % 10) == 0 && g_atomic_int_get (&test_weak_ref_thread_guantlet_waiting)) break; } dex_weak_ref_clear (wr); g_free (wr); return NULL; } static void test_weak_ref_thread_guantlet (void) { TestObject *to = test_object_new (); GThread *threads[8]; finalize_count = 0; for (guint i = 0; i < G_N_ELEMENTS (threads); i++) { DexWeakRef *wr = g_new0 (DexWeakRef, 1); g_autofree char *thread_name = g_strdup_printf ("test_weak_ref_thread_guantlet:%d", i); dex_weak_ref_init (wr, to); threads[i] = g_thread_new (thread_name, test_weak_ref_thread_guantlet_worker, wr); } g_assert_cmpint (g_atomic_int_get (&finalize_count), ==, 0); g_usleep (G_USEC_PER_SEC / 3); g_assert_cmpint (g_atomic_int_get (&finalize_count), ==, 0); dex_unref (to); g_atomic_int_set (&test_weak_ref_thread_guantlet_waiting, TRUE); for (guint i = 0; i < G_N_ELEMENTS (threads); i++) g_thread_join (threads[i]); g_assert_cmpint (finalize_count, ==, 1); } static void test_object_basic (void) { g_assert_true (dex_object_get_type() != G_TYPE_INVALID); g_assert_true (G_TYPE_IS_INSTANTIATABLE (dex_object_get_type())); g_assert_true (G_TYPE_IS_ABSTRACT (dex_object_get_type())); } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/Object/basic", test_object_basic); g_test_add_func ("/Dex/TestSuite/WeakRef/single-threaded", test_weak_ref_st); g_test_add_func ("/Dex/TestSuite/WeakRef/multi-threaded", test_weak_ref_mt); g_test_add_func ("/Dex/TestSuite/WeakRef/extend-liveness", test_weak_ref_extend_liveness); g_test_add_func ("/Dex/TestSuite/WeakRef/immortal", test_weak_ref_immortal); g_test_add_func ("/Dex/TestSuite/WeakRef/thread-guantlet", test_weak_ref_thread_guantlet); return g_test_run (); } 07070100000085000081A40000000000000000000000016712D33C00001210000000000000000000000000000000000000002800000000libdex-0.8.1/testsuite/test-scheduler.c/* test-scheduler.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <libdex.h> static DexScheduler *thread_pool; static GMainLoop *main_loop; static void test_main_scheduler_simple_cb (gpointer data) { gboolean *count = data; *count = 123; g_main_loop_quit (main_loop); } static void test_main_scheduler_simple (void) { DexScheduler *scheduler = dex_scheduler_get_default (); gboolean count = 0; g_assert_nonnull (scheduler); g_assert_true (DEX_IS_MAIN_SCHEDULER (scheduler)); main_loop = g_main_loop_new (NULL, FALSE); dex_scheduler_push (scheduler, test_main_scheduler_simple_cb, &count); g_main_loop_run (main_loop); g_clear_pointer (&main_loop, g_main_loop_unref); g_assert_cmpint (count, ==, 123); } static DexFuture * test_fiber2_func (gpointer user_data) { guint *count = user_data; g_atomic_int_inc (count); return dex_future_new_for_boolean (TRUE); } static DexFuture * test_fiber_func (gpointer user_data) { GPtrArray *all = g_ptr_array_new_with_free_func (dex_unref); for (guint i = 0; i < 10; i++) g_ptr_array_add (all, dex_scheduler_spawn (dex_scheduler_get_thread_default (), dex_get_min_stack_size (), test_fiber2_func, user_data, NULL)); dex_await (dex_future_allv ((DexFuture **)all->pdata, all->len), NULL); g_ptr_array_unref (all); return NULL; } static DexFuture * spawner (gpointer user_data) { GPtrArray *all = g_ptr_array_new_with_free_func (dex_unref); for (guint i = 0; i < 1000; i++) g_ptr_array_add (all, dex_scheduler_spawn (thread_pool, dex_get_min_stack_size (), test_fiber_func, user_data, NULL)); dex_await (dex_future_allv ((DexFuture **)(gpointer)all->pdata, all->len), NULL); g_ptr_array_unref (all); return NULL; } static DexFuture * quit_cb (DexFuture *completed, gpointer user_data) { g_test_message ("Quiting main loop"); g_main_loop_quit (main_loop); return NULL; } static void test_thread_pool_scheduler_spawn (void) { DexFuture *future; guint count = 0; thread_pool = dex_thread_pool_scheduler_new (); main_loop = g_main_loop_new (NULL, FALSE); g_test_message ("Spawning with stack size %u", (guint)dex_get_min_stack_size ()); future = dex_scheduler_spawn (NULL, 0, spawner, &count, NULL); future = dex_future_finally (future, quit_cb, NULL, NULL); g_test_message ("Running main loop"); g_main_loop_run (main_loop); g_assert_cmpint (count, ==, 10*1000); dex_unref (future); dex_unref (thread_pool); } static void test_thread_pool_scheduler_push_cb (gpointer data) { struct { GMutex mutex; GCond cond; } *syncobj = data; g_mutex_lock (&syncobj->mutex); g_cond_signal (&syncobj->cond); g_mutex_unlock (&syncobj->mutex); } static void test_thread_pool_scheduler_push (void) { struct { GMutex mutex; GCond cond; } syncobj; g_mutex_init (&syncobj.mutex); g_cond_init (&syncobj.cond); g_mutex_lock (&syncobj.mutex); dex_scheduler_push (dex_thread_pool_scheduler_get_default (), test_thread_pool_scheduler_push_cb, &syncobj); g_cond_wait (&syncobj.cond, &syncobj.mutex); g_mutex_unlock (&syncobj.mutex); g_mutex_clear (&syncobj.mutex); g_cond_clear (&syncobj.cond); } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/MainScheduler/simple", test_main_scheduler_simple); g_test_add_func ("/Dex/TestSuite/ThreadPoolScheduler/10_000_fibers", test_thread_pool_scheduler_spawn); g_test_add_func ("/Dex/TestSuite/ThreadPoolScheduler/push", test_thread_pool_scheduler_push); return g_test_run (); } 07070100000086000081A40000000000000000000000016712D33C00000DE8000000000000000000000000000000000000002800000000libdex-0.8.1/testsuite/test-semaphore.c/* test-semaphore.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <libdex.h> #include "dex-aio-backend-private.h" #include "dex-main-scheduler-private.h" #include "dex-semaphore-private.h" #include "dex-thread-storage-private.h" #define N_THREADS 32 static guint total_count; static gboolean in_shutdown; static guint done; typedef struct { DexSemaphore *semaphore; GThread *thread; GSource *source; int threadid; int handled; } WorkerState; static DexFuture * worker_thread_callback (DexFuture *future, gpointer user_data) { WorkerState *state = user_data; g_atomic_int_inc (&total_count); state->handled++; if (!g_atomic_int_get (&in_shutdown)) return dex_semaphore_wait (state->semaphore); return NULL; } static gpointer worker_thread_func (gpointer data) { WorkerState *state = data; GMainContext *main_context = g_main_context_new (); DexMainScheduler *main_scheduler = dex_main_scheduler_new (main_context); DexFuture *future; future = dex_semaphore_wait (state->semaphore); future = dex_future_then_loop (future, worker_thread_callback, state, NULL); while (!g_atomic_int_get (&in_shutdown)) g_main_context_iteration (main_context, TRUE); dex_unref (future); while (g_main_context_pending (main_context)) g_main_context_iteration (main_context, FALSE); dex_unref (main_scheduler); g_main_context_unref (main_context); g_atomic_int_inc (&done); return NULL; } static void test_semaphore_threaded (void) { DexSemaphore *semaphore = dex_semaphore_new (); WorkerState state[N_THREADS] = {{0}}; for (guint i = 0; i < G_N_ELEMENTS (state); i++) { char *name = g_strdup_printf ("test-semaphore-%u", i); state[i].semaphore = semaphore; state[i].threadid = i; state[i].thread = g_thread_new (name, worker_thread_func, &state[i]); g_free (name); } g_usleep (G_USEC_PER_SEC/2); for (guint i = 0; i < 3; i++) { int count = 10000; g_atomic_int_set (&total_count, 0); dex_semaphore_post_many (semaphore, count); g_usleep (G_USEC_PER_SEC); g_test_message ("Expected %u, got %u", count, g_atomic_int_get (&total_count)); g_assert_cmpint (g_atomic_int_get (&total_count), ==, count); } g_atomic_int_set (&in_shutdown, TRUE); while (g_atomic_int_get (&done) < N_THREADS) dex_semaphore_post (semaphore); dex_semaphore_close (semaphore); for (guint i = 0; i < G_N_ELEMENTS (state); i++) { g_thread_join (state[i].thread); g_test_message ("Thread %d handled %d items", i, state[i].handled); } } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/Semaphore/threaded", test_semaphore_threaded); return g_test_run (); } 07070100000087000081A40000000000000000000000016712D33C000008BD000000000000000000000000000000000000002500000000libdex-0.8.1/testsuite/test-stream.c/* test-stream.c * * Copyright 2022 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <config.h> #include <libdex.h> #include <gio/gio.h> #include "dex-future-private.h" #define ASSERT_STATUS(f,status) g_assert_cmpint(status, ==, dex_future_get_status(DEX_FUTURE(f))) static DexFuture * quit_cb (DexFuture *future, gpointer user_data) { g_main_loop_quit (user_data); return NULL; } static void test_read_bytes (void) { GFile *file = g_file_new_for_path ("/etc/os-release"); GError *error = NULL; GInputStream *stream = G_INPUT_STREAM (g_file_read (file, NULL, &error)); GMainLoop *main_loop = g_main_loop_new (NULL, FALSE); DexFuture *future; const GValue *value; GBytes *bytes; if (stream == NULL) { g_clear_error (&error); g_test_skip ("/etc/os-release not available"); return; } future = dex_input_stream_read_bytes (stream, 4096, 0); future = dex_future_then (future, quit_cb, main_loop, NULL); g_main_loop_run (main_loop); value = dex_future_get_value (future, &error); g_assert_no_error (error); g_assert_nonnull (value); g_assert_true (G_VALUE_HOLDS (value, G_TYPE_BYTES)); bytes = g_value_get_boxed (value); g_assert_nonnull (bytes); g_main_loop_quit (main_loop); g_main_loop_unref (main_loop); dex_unref (future); } int main (int argc, char *argv[]) { dex_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Dex/TestSuite/InputStream/read_bytes", test_read_bytes); return g_test_run (); } 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1352 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor