Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows Support #1259

Merged
merged 300 commits into from
Nov 22, 2024
Merged

Windows Support #1259

merged 300 commits into from
Nov 22, 2024

Conversation

dgreatwood
Copy link
Collaborator

Addresses: Issue #6, #649, #869, #538, #1128.

The changes for Windows support fall into 5 main buckets:

  1. Build and environment: meson.build, environment configuration (winscripts/*.ps1), and github runner (windows.yaml).
  2. Windows logging support
  3. Syntactic and API difference support
  4. Event-handling support in Windows.
  5. Misc/Other

Taking each in turn:

Build and environment

The meson.build files are adjusted to link Windows libraries; to compile and install Pistache's Windows logging methods (uses custom_target); and to export symbols from the DLL (including dump2def subproject).

Environment configuration: We presume development will be carried out via a PowerShell terminal (local or remote). The files winscripts/msvcsetup.ps1 and winscripts/gccsetup.ps1 are provided to configure development with Visual Studio or GCC respectively. The file Building on Windows.txt gives Windows-specific build instructions.

windows.yaml: Leverages msvcsetup.ps1 and gccsetup.ps1 to set up the build environment and install dependencies. Otherwise works similarly to other Pistache runners (linux.yaml etc.).

Windows logging support

We log using the Windows-preferred "EWT" methodology. The meson.build methods provide for the compilation and installation of the necessary logging manifest etc. At the level of the Pistache code, the PS_LOG_xxxx methods are used, just as per Linux, macOS etc.

pist_syslog.cc: In addition to invoking the Windows logging functions, it also uses the Windows API to monitor the registry property HKCU:\Software\pistacheio\pistache\psLogToStdoutAsWell; by setting this property to 1 (or back to 0), the user can turn on (or off) verbose logging to stdout.

Syntactic and API difference support

There are differences between Linux and Windows in type and constant definitions, API call definitions, and C++ syntax. This causes a lot of small changes across the Pistache code base.

The header file winornix.h defines pre-processor macros and header-file names. By using these definitions, in most cases in the code base we avoid patterns such as:

#ifdef _IS_WINDOWS
    doThis();
#else
    doThat();
#endif

As an example, there is no ssize_t type in Windows. We define PST_SSIZE_T in winornix.h, which works in both Windows and *nix code, and reduce to ssize_t in the *nix case.

In a number of cases, we have provided wrappers (or occasionally larger implementations) for Windows so that we can have Linux-like function interfaces for use in the broader Pistache code base. These "Linux-like functions in Windows" are usually fronted with a preproccessor macro to abstract the Windows vs. *nix differences. As an example, the macro PST_SOCK_BIND is used the same way in Windows and *nix; in Windows it invokes our function pist_sock_bind and in the *nix case it reduces to ::bind. Files that provide these kind of wrapper include:

  • pist_clock_gettime
  • pist_fcntl
  • pist_filefns (read, open)
  • pist_ifaddrs
  • pist_resource (getrusage)
  • pist_sockfns (connect, read, close etc.)
  • pist_strerror_r
  • ps_basename
  • ps_sendfile
  • ps_strl (strlcpy / strlcat)
  • pst_errno.h (to wrap errno.h)

Event-handling support in Windows

Most of the differences between event handling in Windows vs. *nix are already abstracted away by the previously-implemented libevent support. The major remaining item is the lack of edge-triggered events in Windows; Windows is level-triggered only. Code in eventmeth.cc emulates edge-triggered behaviour sufficiently for Pistache, essentially by deactivating a activated event, and then, if appropriate, adding it back to the event polling loop ready for the next activation.

Misc/Other Items of Note

C-style casts. Having introduced additional C-style casts during the Windows-support implementation, we then went through to replace them with C++-style casts. It turned out to be easier to do that replacement for all C-style casts, whether newly introduced or not, so that's what we did.

client.cc: Reworked Transport::handleIncoming; its logic around receiving in multiple pieces (cf. use of totalBytes) didn't work before. Windows seems more prone to using multiple receive events to receive a single incoming datagram.

eventmeth.cc

  • Some rework of how the shutdown of an event-loop works - don't use "event finalize later" when not needed.
  • Deal with lack of CLOEXEC in Windows (mostly by ignoring it)

reactor.cc

  • Additional protection (shutdown_mutex_) during shutdowns. This was an issue exposed in Windows.
  • Code for setting thread description. Has to dynamically load the symbol SetThreadDescription from KernelBase.dll in older versions of Windows.

transport.cc - improvements when using TCP_NODELAY, which Windows requires.

listener.cc - use an atomic counter, not the file descriptor number, to generate an index into the array of handlers for a new connection. File descriptor numbers don't have the nice "increment by one each time" pattern in Windows that they do in Linux.

dump2def - subproject utility to assist in generating symbols for DLL export. This is needed becasue, by default, Windows DLLs do not export their symbols, unlike in Linux.

http_server_test.cc

  • Got rid of class SimpleLogger; improved ScopedLogger. This makes it easier to integrate with Windows logging.
  • Deal with the different temporary-file mechanisms in Windows
  • Normalize some indentation (been avoiding this because it shows as a lot of changes, but it's time)

github test runners, which was showing up as memory leaks when using
force-libevent.
EmEvent leak in case of DEBUG and libevent on.

In http_server_test, for every test case encapsulating the body of
each test to guarantee that local (stack) variables go out of scope
before the google AddressSanitizer has a chance to test for memory
leaks.
covers the whole of the test (not just the interior/"thread"-variable
part)
In streaming_test, check for no EmEvent leaks solely in the final test
("ClientDisconnect")
define in pist_syslog.h)

Tracking EmEvents in streaming_test
to a shared_ptr. Previously, the reference returned by getPeer was to
a shared_ptr stored in the map peers_ - so if the entry was removed
from peers_, or peers_ went out of scope, the reference could refer to
a shared_ptr that no longer existed.

Rather than closing a peer's Fd directly in transport, now call
peer->closeFd(), which not only closes the Fd but allows peer to set
it's fd_ to empty, helping ensure that the Fd is closed exactly once.
missing close was seen in certain environments (though not all
environments) for streaming_test.ClientDisconnect. It reproduced
reliably in github workflow using current linux.yaml and
os: debian:stable; compiler: gcc; sanitizer: address; tls: true; flibev: true
Put back the exlcuded tests in streaming_test
for a libevent callback function (the callback function will result in
the EmEvent being placed on ready_).

Change linux.yaml to run solely http_server_test for the timebeing
the whole of shutdown. Otherwise we can have a two-mutex deadlock
between queuesLock and handling_mutex during shutdown. See code
comment for more details.

Move my_basename_r into pist_syslog.h so it can be used by other code
beyond eventmeth.cc.

Moved GUARD_AND_DBG_LOG into pist_check.h, so it can be used by other
code beyond eventmeth.cc.
Created a new linuxflibev.yaml which takes care of running the
libevent builds and test cases
avoid any possible confusion with the regular linux tests.
so that it returns with interest_mutex_ locked; the caller (in os.cc)
is then required to call unlockInterestMutexIfLocked()
thereafter. This is used to prevent another thread closing (and
invalidating) an Fd that has been returned to os.cc's Epoll::poll
before Epoll::poll can finish processing the returned Fd.

fix: In getReadyEmEvents, adding a short, local lock of ready_mutex_ to
protect the ready_ set as we get and remove it's contents.

docs: Added notes on other non-libevent-specific fixes to "macOS
Implementation.txt"

style: Corrected comment typo in client.cc
If this option is set to "true", then all logged messages will be sent
to stdout in addition to the log file.
We are now calling lockInterestMutex *before* we grab the contents of
ready_ in getReadyEmEventsHelper. That way, since the interest_mutex_
is required for closing an Fd, there is no risk of an Fd closing
between ready_ being grabbed (ready_ could contain the Fd_) and
lockInterestMutex being called.

Updated some debug output in os.cc.
@kiplingw kiplingw self-requested a review November 11, 2024 00:02
@kiplingw kiplingw requested a review from Tachi107 November 11, 2024 00:05
@kiplingw kiplingw added help wanted portability fix in progress dependencies Pull requests that update a dependency file experimental unit / CI testing For unit testing and CI (including DEP-8) labels Nov 11, 2024
winscripts/gccsetup.ps1 + winscripts/msvcsetup.ps1 prefix the
PowerShell prompt with "GCC " or "MVS " respectively, if not already
so prefixed. In checking the existing prompt for GCC/MVS, the
powershell code was throwing an exception if the existing prompt was
shorter than 3 chars. Now coping correctly with that case.
Copy link

codecov bot commented Nov 11, 2024

Codecov Report

Attention: Patch coverage is 77.92793% with 98 lines in your changes missing coverage. Please review.

Project coverage is 75.10%. Comparing base (1e9b906) to head (31134c2).
Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
src/server/listener.cc 75.86% 14 Missing ⚠️
src/common/ps_strl.cc 66.66% 12 Missing ⚠️
src/client/client.cc 82.45% 10 Missing ⚠️
src/common/http.cc 82.22% 8 Missing ⚠️
src/common/net.cc 69.23% 8 Missing ⚠️
src/common/pist_check.cc 0.00% 8 Missing ⚠️
include/pistache/serializer/rapidjson.h 0.00% 7 Missing ⚠️
include/pistache/async.h 78.94% 4 Missing ⚠️
src/common/pist_syslog.cc 89.47% 4 Missing ⚠️
include/pistache/transport.h 57.14% 3 Missing ⚠️
... and 11 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1259      +/-   ##
==========================================
- Coverage   77.93%   75.10%   -2.83%     
==========================================
  Files          57       58       +1     
  Lines        7914     8943    +1029     
==========================================
+ Hits         6168     6717     +549     
- Misses       1746     2226     +480     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

We wanted to fix this for Windows, and just fixed it for the other OS
at the same time.

windows.yaml
linux.yaml
linuxflibev.yaml
macos.yaml
  - Add brotli and zstd to dependencies installed by the yaml file
    (In windows, this is done in commonsetup.ps1, which is invoked by
    gccsetup.ps1 and msvcsetup.ps1, which in turn are invoked by
    windows.yaml)
    (In linux.yaml, added for both Debian and RedHat dependency
    install)
  - Enable brotli and zstd encoding in "meson setup..." line (This
    also ensures that the brotli and zstd tests are built in
    http_server_test.cc)

commonsetup.ps1 (Windows only - used in windows.yaml, see above)
  - Install brotli and zstd with vcpkg if not already present

Building on macOS.txt
Building on Windows.txt
Building on BSD - FreeBSD, OpenBSD and NetBSD.txt
  - Updated to reflect brotli and zstd dependencies

Convenience scripts:
winscripts/mesbuild.ps1
winscripts/mesbuilddebug.ps1
bldscripts/mesbuild.sh
bldscripts/mesbuilddebug.sh
bldscripts/mesbuildflibev.sh
bldscripts/mesbuildflibevdebug.sh
  - Updated so deflate, brotli and zstd meson setup options are all
    defined to true for "meson setup ...".

http_server_test
  - When filling with random data for deflate, zstd, or brotli tests,
    use a std::vector<unsigned short>, not a std:vector of
    bytes. Support for the vector of bytes is not actually required by
    the standard, and causes an error on MSVC/Windows.
  - Add a check to the return code of ZSTD_getFrameContentSize and
    adjust appropriately if it is an error (previously, was treating
    the error code as though it was a length).
  - Pass the parameters to ZSTD_decompress in the correct order
  - Revert some indentation to the existing indentation used in master
    branch

Also:
bldscripts/clean.sh: Update to find correct build dir(s)
linux.yaml
linuxflibev.yaml
 - For Debian builds, install libbrotli-dev and libzstd-dev
 - For RedHat builds, install libbrotli and libzstd

Building on BSD - FreeBSD, OpenBSD and NetBSD.txt
README.md
 - Updated to reflect the new libxxxx dependencies

Also, http_server_test.cc:
 - Used pointer arithmetic rather than &(*std::end()) to get pointer
   to immediately-after the end of an array. Some versions of MSVC
   object to "&(*std::end())".
For brotli, we install the library brotli-devel

There doesn't seem to be a working preconfigured zstd package for
RedHat ubi8 or ubi9. We skip linking and testing zstd compression for
RedHat builds.
In particular, passing the correct buffer to ZSTD_getFrameContentSize.
Following review of the windows PR pistacheio#1259

Include compression features -
ZSTD, Brotli and Deflate all included in github runner tests
Similarly included them in the  mesbuild* convenience scripts

client.cc:
715: Make dynamic receive buffer maximum smaller
Use std::memcpy not mempy

Don't use NULL, prefer nullptr thoughout

net.cc: 346
tmp2 - set it to NULL
check strtol ret code
check tmp2 before dereferencing it

winornix.h and throughout:
Use macros for the se_err[256+16] strerror_r buffer stuff
Use MAXPATHLEN for size for strerror_r buffer

pist_ifaddrs.cc:120 and throughout:
When it doesn't matter, use prefix ++ or --, not postfix
  (Changed this in my own code, mostly left it alone in other code)

pist_ifaddrs.cc
218: declare name_len const
219, 234: remove C-casts

pist_resource.cc:
Remove C casts

pist_strerror_r.cc
37: Put ::strcmp. Same for 49, 51, 53

pist_syslog.cc:
344 Make get_val_res const
Also 371
Also 582, 1100, 1126

ps_basename.cc
104: Remove C-cast

ps_sendfile.cc
75, 84: Remove C-cast

ps_strl.cc
54, 58: set as const

reactor.cc
40: lLoggedSetThreadDescriptionFail should be static. Also 47-49.
721-724: Make const

transport.cc
1113: const_cast - made it mutable instead

src/meson.build
111: Include windres in the comment

pkgconfig - check pistachelog.dll is included. Looks like it
should work. Added comment in meson.build.
256: Add comment on what warning 4102 is

listener.cc:
975: Windows file handle not _always_ a multiple of 4. Add comment.

installman.ps1:
Add a summary comment at the top

async_test.cc
296: First two parms should be cbegin, cend

tests/http_server_test.cc
577: Replace "= 0" with "= nullptr"

tests/listener_test.cc:
Call strerror_r to get the "address in use" string to compare to
405: Don't Use (void). (switched to std::ignore)

In scripts, move helpers into a "helpers" subdir
Use location of script to determine location of helpers (Don't
hardcode winscripts/helpers or bldscripts/helpers).
include/pistache/winornix.h:
Previously, was using PST_MAXPATHLEN without including the necessary
header file. Corrected.

bldscripts/helpers/mesflibevsetdirvars.sh:
Added a missing variable definition in the script
Also:
In src/meson.build:
Provide vs_module_defs parameter to meson "libpistache = library(..."
command for Visual Studio case.
Newer headers (like pist_strerror_r.h) added to install_headers in
/include/pistache/meson.build.
winornix.h, pist_strerror_r.h, pist_strerror_r.cc: Wrap strerror_r for
Apple GCC case (Apple GCC defaults to XSI strerror_r, which is not the
strerror_r we want). Fixes compile error for GCC on macOS.

Also:
winornix.h: we wrap errno.h with pst_errno.h in all non-Linux
cases. Fixes a compile issue on openBSD.
Copy link
Member

@Tachi107 Tachi107 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Duncan, thanks for your incredible work. Windows support is something many people wanted and this patch set will make them quite happy. I don't know how many people will actually run Pistache on a production environment with Windows, but still, portability is a worthwhile goal in itself, especially when it comes to supporting non-Unix systems. Unix made lot of things right, but it's not perfect (my bachelor's thesis is partly about this, in fact).

With that being said, I'm not going to review this. There are more then ten thousand changed lines, spanning over 300 commits, and it is hence impossible for anyone to review such work in one's free time. I would have preferred small, organized changes sent over time via multiple pull requests.

Kip, I leave this to you :)

Thanks again for your incredible work!

@dgreatwood
Copy link
Collaborator Author

Hi @Tachi107 -

Thank you for your consideration :-)

Yes, indeed, this PR is essentially a large number of tiny changes to address syntactical and API matters, combined with some lumps of PowerShell script to configure the dev environment.

Should you have additional comments related to the changes in future, I will of course be more than happy to take a look.

Meanwhile, all the best!

@kiplingw kiplingw merged commit f955e9e into pistacheio:master Nov 22, 2024
147 of 150 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file experimental fix in progress help wanted portability unit / CI testing For unit testing and CI (including DEP-8)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants