maintenance: create `launchctl` configuration using a lock file
When two `git maintenance` processes try to write the `.plist` file, we need to help them with serializing their efforts. The 150ms time-out value was determined from thin air. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Johannes Schindelin committed
Aug 24, 2021 at 15:43 UTC
bb01122a82002dc5948147800da40a149779a7b1
1 file changed
+30
-17
builtin/gc.c
+30
-17
@@ -1602,16 +1602,14 @@ static int launchctl_remove_plists(const char *cmd)
1602
1603
static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
1604
{
1605
- FILE *plist;
1606
- int i;
1605
+ int i, fd;
1606
const char *preamble, *repeat;
1607
const char *frequency = get_frequency(schedule);
1608
char *name = launchctl_service_name(frequency);
1609
char *filename = launchctl_service_filename(name);
1611
-
1612
- if (safe_create_leading_directories(filename))
1613
- die(_("failed to create directories for '%s'"), filename);
1614
- plist = xfopen(filename, "w");
1610
+ struct lock_file lk = LOCK_INIT;
1611
+ static unsigned long lock_file_timeout_ms = ULONG_MAX;
1612
+ struct strbuf plist = STRBUF_INIT;
1613
1614
preamble = "<?xml version=\"1.0\"?>\n"
1615
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
@@ -1630,7 +1628,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
1628
"</array>\n"
1629
"<key>StartCalendarInterval</key>\n"
1630
"<array>\n";
1633
- fprintf(plist, preamble, name, exec_path, exec_path, frequency);
1631
+ strbuf_addf(&plist, preamble, name, exec_path, exec_path, frequency);
1632
1633
switch (schedule) {
1634
case SCHEDULE_HOURLY:
@@ -1639,7 +1637,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
1637
"<key>Minute</key><integer>0</integer>\n"
1638
"</dict>\n";
1639
for (i = 1; i <= 23; i++)
1642
- fprintf(plist, repeat, i);
1640
+ strbuf_addf(&plist, repeat, i);
1641
break;
1642
1643
case SCHEDULE_DAILY:
@@ -1649,24 +1647,38 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
1647
"<key>Minute</key><integer>0</integer>\n"
1648
"</dict>\n";
1649
for (i = 1; i <= 6; i++)
1652
- fprintf(plist, repeat, i);
1650
+ strbuf_addf(&plist, repeat, i);
1651
break;
1652
1653
case SCHEDULE_WEEKLY:
1656
- fprintf(plist,
1657
- "<dict>\n"
1658
- "<key>Day</key><integer>0</integer>\n"
1659
- "<key>Hour</key><integer>0</integer>\n"
1660
- "<key>Minute</key><integer>0</integer>\n"
1661
- "</dict>\n");
1654
+ strbuf_addstr(&plist,
1655
+ "<dict>\n"
1656
+ "<key>Day</key><integer>0</integer>\n"
1657
+ "<key>Hour</key><integer>0</integer>\n"
1658
+ "<key>Minute</key><integer>0</integer>\n"
1659
+ "</dict>\n");
1660
break;
1661
1662
default:
1663
/* unreachable */
1664
break;
1665
}
1668
- fprintf(plist, "</array>\n</dict>\n</plist>\n");
1669
- fclose(plist);
1666
+ strbuf_addstr(&plist, "</array>\n</dict>\n</plist>\n");
1667
+
1668
+ if (safe_create_leading_directories(filename))
1669
+ die(_("failed to create directories for '%s'"), filename);
1670
+
1671
+ if ((long)lock_file_timeout_ms < 0 &&
1672
+ git_config_get_ulong("gc.launchctlplistlocktimeoutms",
1673
+ &lock_file_timeout_ms))
1674
+ lock_file_timeout_ms = 150;
1675
+
1676
+ fd = hold_lock_file_for_update_timeout(&lk, filename, LOCK_DIE_ON_ERROR,
1677
+ lock_file_timeout_ms);
1678
+
1679
+ if (write_in_full(fd, plist.buf, plist.len) < 0 ||
1680
+ commit_lock_file(&lk))
1681
+ die_errno(_("could not write '%s'"), filename);
1682
1683
/* bootout might fail if not already running, so ignore */
1684
launchctl_boot_plist(0, filename, cmd);
@@ -1675,6 +1687,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
1687
1688
free(filename);
1689
free(name);
1690
+ strbuf_release(&plist);
1691
return 0;
1692
}
1693