From 513200cf76758de4668312c628d6362bdabfaf4b Mon Sep 17 00:00:00 2001
From: Alexander Kanavin <alex.kanavin@gmail.com>
Date: Thu, 25 May 2017 19:30:20 +0300
Subject: [PATCH 1/3] Run binary package creation via thread pools.

Upstream-Status: Submitted [https://github.com/rpm-software-management/rpm/pull/226]
Signed-off-by: Alexander Kanavin <alex.kanavin@gmail.com>

---
 build/pack.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++-----------
 configure.ac |  3 +++
 2 files changed, 70 insertions(+), 14 deletions(-)

diff --git a/build/pack.c b/build/pack.c
index ccfd614cc..ed5b9ab4e 100644
--- a/build/pack.c
+++ b/build/pack.c
@@ -616,25 +616,78 @@ static rpmRC packageBinary(rpmSpec spec, Package pkg, const char *cookie, int ch
 	return rc;
 }
 
-rpmRC packageBinaries(rpmSpec spec, const char *cookie, int cheating)
+struct binaryPackageTaskData
 {
-    rpmRC rc;
     Package pkg;
+    char *filename;
+    rpmRC result;
+    struct binaryPackageTaskData *next;
+};
+
+static struct binaryPackageTaskData* runBinaryPackageTasks(rpmSpec spec, const char *cookie, int cheating)
+{
+    struct binaryPackageTaskData *tasks = NULL;
+    struct binaryPackageTaskData *task = NULL;
+    struct binaryPackageTaskData *prev = NULL;
+
+    for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+        task = rcalloc(1, sizeof(*task));
+        task->pkg = pkg;
+        if (pkg == spec->packages) {
+            // the first package needs to be processed ahead of others, as they copy
+            // changelog data from it, and so otherwise data races would happen
+            task->result = packageBinary(spec, pkg, cookie, cheating, &(task->filename));
+            rpmlog(RPMLOG_NOTICE, _("Finished binary package job, result %d, filename %s\n"), task->result, task->filename);
+            tasks = task;
+        }
+        if (prev != NULL) {
+            prev->next = task;
+        }
+        prev = task;
+    }
+
+    #pragma omp parallel
+    #pragma omp single
+    // re-declaring task variable is necessary, or older gcc versions will produce code that segfaults
+    for (struct binaryPackageTaskData *task = tasks; task != NULL; task = task->next) {
+        if (task != tasks)
+        #pragma omp task
+        {
+            task->result = packageBinary(spec, task->pkg, cookie, cheating, &(task->filename));
+            rpmlog(RPMLOG_NOTICE, _("Finished binary package job, result %d, filename %s\n"), task->result, task->filename);
+        }
+    }
+
+    return tasks;
+}
+
+static void freeBinaryPackageTasks(struct binaryPackageTaskData* tasks)
+{
+    while (tasks != NULL) {
+        struct binaryPackageTaskData* next = tasks->next;
+        rfree(tasks->filename);
+        rfree(tasks);
+        tasks = next;
+    }
+}
+
+rpmRC packageBinaries(rpmSpec spec, const char *cookie, int cheating)
+{
     char *pkglist = NULL;
 
-    for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
-	char *fn = NULL;
-	rc = packageBinary(spec, pkg, cookie, cheating, &fn);
-	if (rc == RPMRC_OK) {
-	    rstrcat(&pkglist, fn);
-	    rstrcat(&pkglist, " ");
-	}
-	free(fn);
-	if (rc != RPMRC_OK) {
-	    pkglist = _free(pkglist);
-	    return rc;
-	}
+    struct binaryPackageTaskData *tasks = runBinaryPackageTasks(spec, cookie, cheating);
+
+    for (struct binaryPackageTaskData *task = tasks; task != NULL; task = task->next) {
+        if (task->result == RPMRC_OK) {
+            rstrcat(&pkglist, task->filename);
+            rstrcat(&pkglist, " ");
+        } else {
+            _free(pkglist);
+            freeBinaryPackageTasks(tasks);
+            return RPMRC_FAIL;
+        }
     }
+    freeBinaryPackageTasks(tasks);
 
     /* Now check the package set if enabled */
     if (pkglist != NULL) {
diff --git a/configure.ac b/configure.ac
index a506ec819..59fa0acaf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,6 +17,9 @@ AC_DISABLE_STATIC
 
 PKG_PROG_PKG_CONFIG
 
+AC_OPENMP
+RPMCFLAGS="$OPENMP_CFLAGS $RPMCFLAGS"
+
 dnl Checks for programs.
 AC_PROG_CXX
 AC_PROG_AWK
-- 
2.11.0