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

Run test suite in parallel #2507

Merged
merged 23 commits into from
Nov 16, 2023
Merged

Run test suite in parallel #2507

merged 23 commits into from
Nov 16, 2023

Conversation

paulcadman
Copy link
Collaborator

@paulcadman paulcadman commented Nov 9, 2023

Overview

This PR makes the compiler pipeline thread-safe so that the test suite can be run in parallel.

This is achieved by:

  • Removing use of {get, set, with}CurrentDir functions.
  • Adding locking around shared file resources like the the global-project and internal build directory.

NB: Locking is disabled for the main compiler target, as it is single threaded they are not required.

Run test suite in parallel

To run the test suite in parallel you must add --ta '+RTS -N -RTS' to your stack test arguments. For example:

stack test --fast --ta '+RTS -N -RTS'

The -N instructs the Haskell runtime to choose the number of threads to use based on how many processors there are on your machine. You can use -Nn to see the number of threads to n.

These flags are already set in the Makefile when you or CI uses stack test.

Locking

The Haskell package filelock is used for locking. File locks are used instead of MVars because Juvix code does not control when new threads are created, they are created by the test suite. This means that MVars created by Juvix code will have no effect, because they are created independently on each test-suite thread. Additionally the resources we're locking live on the filesystem and so can be conveniently tagged by path.

FileLock

The filelock library is wrapped in a FileLock effect:

data FileLock m a where
WithFileLock' :: Path Abs File -> m a -> FileLock m a

There is an IO interpreter that uses filelock and an no-op interpreter that just runs actions unconditionally.

TaggedLock

To make the file locks simpler to use a TaggedLock effect is introduced:

-- | An effect that wraps an action with a lock that is tagged with a relative
-- path.
--
-- The relative path does not need to exist in the filesystem.
data TaggedLock m a where
WithTaggedLock :: Path Rel File -> m a -> TaggedLock m a

And convenience function:

withTaggedLockDir :: (Member TaggedLock r) => Path Abs Dir -> Sem r a -> Sem r a

This allows an action to be locked, tagged by a directory that may or may not exist. For example in the following code, an action is performed on a directory root that may delete the directory before repopulating the files. So the lockfile cannot be stored in the root itself.

withTaggedLockDir root . whenM shouldUpdate $ do
whenM
(directoryExists' root)
(removeDirectoryRecursive' root)
writeVersion
action

Pipeline

As noted above, we only use locking in the test suite. The main app target pipeline is single threaded and so locking is unnecessary. So the interpretation of locks is parameterised so that locking can be disabled

. runTaggedLock lockMode

@paulcadman paulcadman marked this pull request as ready for review November 16, 2023 10:37
@janmasrovira janmasrovira merged commit 2f4a3f8 into main Nov 16, 2023
4 checks passed
@janmasrovira janmasrovira deleted the parallel-fast-tests branch November 16, 2023 15:19
@jonaprieto jonaprieto added this to the 0.5.4 milestone Nov 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants