tdf#143148 Use pragma once instead of include guards
[LibreOffice.git] / solenv / bin / job-limiter.cpp
blobee1bd8e6edabdd1e6f4470d29b6da077e124ed65
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 // Helper to reduce parallelism, specifically for msi installset packaging
12 // Ideally it would hook into make's jobserver, but it is too easy to deadlock the build at the
13 // packaging stage (all jobs are started at once, consuming all available slots as part of general
14 // parallelism, and then each job waiting for an additional job token but since all jobs are in
15 // waiting stage: you have a deadlock).
16 // That in turn is an advantage for a simple approach with separate lock and release commands, since
17 // everything is started at once, and all jobs that will be queued are all jobs that use the helper,
18 // there will be jobs in waiting state and keeping the semaphore active as long as it matters.
20 #include <iostream>
21 #include <windows.h>
23 using namespace std;
25 int grab(HANDLE& semaphore_handle)
27 DWORD waitresult = WaitForSingleObject(semaphore_handle, INFINITE);
29 switch (waitresult)
31 case WAIT_OBJECT_0:
32 return 0;
33 case WAIT_TIMEOUT:
34 fprintf(stderr, "grabbing a slot failed with timeout despite waiting for INFINITE?\n");
35 break;
36 case WAIT_FAILED:
37 fprintf(stderr, "grabbing a slot failed with error: %d\n", GetLastError());
38 break;
39 default:
40 fprintf(stderr, "grabbing a slot failed with status %d - error %d", waitresult,
41 GetLastError());
43 return 1;
46 int release(HANDLE& semaphore_handle)
48 if (ReleaseSemaphore(semaphore_handle, 1, NULL))
50 return 0;
52 else
54 fprintf(stderr, "something went wrong trying to release a slot: %d\n", GetLastError());
55 return 1;
59 int wmain(int argc, wchar_t* argv[])
61 if (argc != 2)
63 fprintf(stderr, "Invalid number of arguments!\n");
64 fprintf(stderr, "call with 'grab' or 'release'\n");
65 exit(1);
67 // since the reason for limiting the parallelism is the instability with cscript/WiLangId.vbs
68 // calls and high parallelism, not having that unique/potentially sharing the limit semaphore
69 // with another build is not a problem but considered a feature...
70 LPCWSTR semaphorename = L"lo_installset_limiter";
71 HANDLE semaphore_handle
72 = OpenSemaphoreW(SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, false, semaphorename);
73 if (semaphore_handle == NULL)
75 fprintf(stdout, "no existing semaphore, creating new one\n");
76 // Need to have a buffer slot, since the helper isn't persistent.
77 // When it is called to release a slot it might be that all holders of the semaphore
78 // are gone already and thus the semaphore also gets closed and is no longer present on the
79 // system. So when it creates a new one to use, and then releases one it would hit the max
80 // limit otherwise. This only happens when nothing else is waiting for a slot anymore,
81 // i.e. when there are already fewer jobs than imposed by the limiter.
82 // A limit of three (main installer + 2 controlled by this limiter) was chosen because that
83 // won't affect overall build time (creating the main installer with multiple languages
84 // takes much longer than all the helppackages and the single sdk package combined, even
85 // when those are limited to three jobs), and seems to be low enough to avoid the random
86 // cscript/WiLangId.vbs failures.
87 semaphore_handle = CreateSemaphoreW(NULL, 2, 3, semaphorename);
88 // keep this process alive for other jobs to grab the semaphore, otherwise it is gone too
89 // quickly and everything creates their own semaphore that immediately has enough slots,
90 // completely bypassing the point of having a limiter...
91 Sleep(500);
93 if (semaphore_handle == NULL)
95 fprintf(stderr, "Error creating/opening the semaphore - bailing out (%d)\n",
96 GetLastError());
97 exit(1);
100 if (wcscmp(argv[1], L"grab") == 0)
102 exit(grab(semaphore_handle));
104 else if (wcscmp(argv[1], L"release") == 0)
106 exit(release(semaphore_handle));
108 else
110 fwprintf(stderr, L"invalid action '%s'\nSupported actions are 'grab' or 'release'\n",
111 argv[1]);
113 exit(1);
116 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */