Git fork

date: make DATE_MODE thread-safe

date_mode_from_type() modifies a static variable and returns a pointer
to it. This is not thread-safe. Most callers of date_mode_from_type()
use it via the macro DATE_MODE and pass its result on to functions like
show_date(), which take a const pointer and don't modify the struct.

Avoid the static storage by putting the variable on the stack and
returning the whole struct date_mode. Change functions that take a
constant pointer to expect the whole struct instead.

Reduce the cost of passing struct date_mode around on 64-bit systems
by reordering its members to close the hole between the 32-bit wide
.type and the 64-bit aligned .strftime_fmt as well as the alignment
hole at the end. sizeof reports 24 before and 16 with this change
on x64. Keep .type at the top to still allow initialization without
designator -- though that's only done in a single location, in
builtin/blame.c.

Signed-off-by: René Scharfe <l.s.r@web.de>
Acked-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

René Scharfe and committed by
Junio C Hamano
9720d23e 1f49f750

+44 -44
+2 -2
builtin/blame.c
··· 316 316 size_t time_width; 317 317 int tz; 318 318 tz = atoi(tz_str); 319 - time_str = show_date(time, tz, &blame_date_mode); 319 + time_str = show_date(time, tz, blame_date_mode); 320 320 strbuf_addstr(&time_buf, time_str); 321 321 /* 322 322 * Add space paddings to time_buf to display a fixed width ··· 1029 1029 blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700"); 1030 1030 break; 1031 1031 case DATE_STRFTIME: 1032 - blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */ 1032 + blame_date_width = strlen(show_date(0, 0, blame_date_mode)) + 1; /* add the null */ 1033 1033 break; 1034 1034 } 1035 1035 blame_date_width -= 1; /* strip the null */
+18 -18
date.c
··· 207 207 (diff + 183) / 365); 208 208 } 209 209 210 - struct date_mode *date_mode_from_type(enum date_mode_type type) 210 + struct date_mode date_mode_from_type(enum date_mode_type type) 211 211 { 212 - static struct date_mode mode = DATE_MODE_INIT; 212 + struct date_mode mode = DATE_MODE_INIT; 213 213 if (type == DATE_STRFTIME) 214 214 BUG("cannot create anonymous strftime date_mode struct"); 215 215 mode.type = type; 216 - return &mode; 216 + return mode; 217 217 } 218 218 219 219 static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm, int tz, struct tm *human_tm, int human_tz, int local) ··· 283 283 strbuf_addf(buf, " %+05d", tz); 284 284 } 285 285 286 - const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) 286 + const char *show_date(timestamp_t time, int tz, struct date_mode mode) 287 287 { 288 288 struct tm *tm; 289 289 struct tm tmbuf = { 0 }; ··· 291 291 int human_tz = -1; 292 292 static struct strbuf timebuf = STRBUF_INIT; 293 293 294 - if (mode->type == DATE_UNIX) { 294 + if (mode.type == DATE_UNIX) { 295 295 strbuf_reset(&timebuf); 296 296 strbuf_addf(&timebuf, "%"PRItime, time); 297 297 return timebuf.buf; 298 298 } 299 299 300 - if (mode->type == DATE_HUMAN) { 300 + if (mode.type == DATE_HUMAN) { 301 301 struct timeval now; 302 302 303 303 get_time(&now); ··· 306 306 human_tz = local_time_tzoffset(now.tv_sec, &human_tm); 307 307 } 308 308 309 - if (mode->local) 309 + if (mode.local) 310 310 tz = local_tzoffset(time); 311 311 312 - if (mode->type == DATE_RAW) { 312 + if (mode.type == DATE_RAW) { 313 313 strbuf_reset(&timebuf); 314 314 strbuf_addf(&timebuf, "%"PRItime" %+05d", time, tz); 315 315 return timebuf.buf; 316 316 } 317 317 318 - if (mode->type == DATE_RELATIVE) { 318 + if (mode.type == DATE_RELATIVE) { 319 319 strbuf_reset(&timebuf); 320 320 show_date_relative(time, &timebuf); 321 321 return timebuf.buf; 322 322 } 323 323 324 - if (mode->local) 324 + if (mode.local) 325 325 tm = time_to_tm_local(time, &tmbuf); 326 326 else 327 327 tm = time_to_tm(time, tz, &tmbuf); ··· 331 331 } 332 332 333 333 strbuf_reset(&timebuf); 334 - if (mode->type == DATE_SHORT) 334 + if (mode.type == DATE_SHORT) 335 335 strbuf_addf(&timebuf, "%04d-%02d-%02d", tm->tm_year + 1900, 336 336 tm->tm_mon + 1, tm->tm_mday); 337 - else if (mode->type == DATE_ISO8601) 337 + else if (mode.type == DATE_ISO8601) 338 338 strbuf_addf(&timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d", 339 339 tm->tm_year + 1900, 340 340 tm->tm_mon + 1, 341 341 tm->tm_mday, 342 342 tm->tm_hour, tm->tm_min, tm->tm_sec, 343 343 tz); 344 - else if (mode->type == DATE_ISO8601_STRICT) { 344 + else if (mode.type == DATE_ISO8601_STRICT) { 345 345 strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d", 346 346 tm->tm_year + 1900, 347 347 tm->tm_mon + 1, ··· 354 354 tz = abs(tz); 355 355 strbuf_addf(&timebuf, "%02d:%02d", tz / 100, tz % 100); 356 356 } 357 - } else if (mode->type == DATE_RFC2822) 357 + } else if (mode.type == DATE_RFC2822) 358 358 strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d", 359 359 weekday_names[tm->tm_wday], tm->tm_mday, 360 360 month_names[tm->tm_mon], tm->tm_year + 1900, 361 361 tm->tm_hour, tm->tm_min, tm->tm_sec, tz); 362 - else if (mode->type == DATE_STRFTIME) 363 - strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, 364 - !mode->local); 362 + else if (mode.type == DATE_STRFTIME) 363 + strbuf_addftime(&timebuf, mode.strftime_fmt, tm, tz, 364 + !mode.local); 365 365 else 366 - show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode->local); 366 + show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode.local); 367 367 return timebuf.buf; 368 368 } 369 369
+3 -3
date.h
··· 22 22 23 23 struct date_mode { 24 24 enum date_mode_type type; 25 - const char *strftime_fmt; 26 25 int local; 26 + const char *strftime_fmt; 27 27 }; 28 28 29 29 #define DATE_MODE_INIT { \ ··· 36 36 * show_date(t, tz, DATE_MODE(NORMAL)); 37 37 */ 38 38 #define DATE_MODE(t) date_mode_from_type(DATE_##t) 39 - struct date_mode *date_mode_from_type(enum date_mode_type type); 39 + struct date_mode date_mode_from_type(enum date_mode_type type); 40 40 41 41 /** 42 42 * Format <'time', 'timezone'> into static memory according to 'mode' 43 43 * and return it. The mode is an initialized "struct date_mode" 44 44 * (usually from the DATE_MODE() macro). 45 45 */ 46 - const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode); 46 + const char *show_date(timestamp_t time, int timezone, struct date_mode mode); 47 47 48 48 /** 49 49 * Parse a date format for later use with show_date().
+1 -1
gpg-interface.c
··· 483 483 484 484 if (sigc->payload_timestamp) 485 485 strbuf_addf(&verify_time, "-Overify-time=%s", 486 - show_date(sigc->payload_timestamp, 0, &verify_date_mode)); 486 + show_date(sigc->payload_timestamp, 0, verify_date_mode)); 487 487 488 488 /* Find the principal from the signers */ 489 489 strvec_pushl(&ssh_keygen.args, fmt->program,
+1 -1
log-tree.c
··· 777 777 */ 778 778 show_reflog_message(opt->reflog_info, 779 779 opt->commit_format == CMIT_FMT_ONELINE, 780 - &opt->date_mode, 780 + opt->date_mode, 781 781 opt->date_mode_explicit); 782 782 if (opt->commit_format == CMIT_FMT_ONELINE) 783 783 return;
+3 -3
oss-fuzz/fuzz-date.c
··· 11 11 int16_t tz; 12 12 timestamp_t ts; 13 13 enum date_mode_type dmtype; 14 - struct date_mode *dm; 14 + struct date_mode dm; 15 15 16 16 if (size <= 4) 17 17 /* ··· 40 40 free(str); 41 41 42 42 dm = date_mode_from_type(dmtype); 43 - dm->local = local; 43 + dm.local = local; 44 44 show_date(ts, (int)tz, dm); 45 45 46 - date_mode_release(dm); 46 + date_mode_release(&dm); 47 47 48 48 return 0; 49 49 }
+9 -9
pretty.c
··· 428 428 } 429 429 430 430 const char *show_ident_date(const struct ident_split *ident, 431 - const struct date_mode *mode) 431 + struct date_mode mode) 432 432 { 433 433 timestamp_t date = 0; 434 434 long tz = 0; ··· 592 592 switch (pp->fmt) { 593 593 case CMIT_FMT_MEDIUM: 594 594 strbuf_addf(sb, "Date: %s\n", 595 - show_ident_date(&ident, &pp->date_mode)); 595 + show_ident_date(&ident, pp->date_mode)); 596 596 break; 597 597 case CMIT_FMT_EMAIL: 598 598 case CMIT_FMT_MBOXRD: ··· 601 601 break; 602 602 case CMIT_FMT_FULLER: 603 603 strbuf_addf(sb, "%sDate: %s\n", what, 604 - show_ident_date(&ident, &pp->date_mode)); 604 + show_ident_date(&ident, pp->date_mode)); 605 605 break; 606 606 default: 607 607 /* notin' */ ··· 775 775 776 776 static size_t format_person_part(struct strbuf *sb, char part, 777 777 const char *msg, int len, 778 - const struct date_mode *dmode) 778 + struct date_mode dmode) 779 779 { 780 780 /* currently all placeholders have same length */ 781 781 const int placeholder_len = 2; ··· 1034 1034 static int format_reflog_person(struct strbuf *sb, 1035 1035 char part, 1036 1036 struct reflog_walk_info *log, 1037 - const struct date_mode *dmode) 1037 + struct date_mode dmode) 1038 1038 { 1039 1039 const char *ident; 1040 1040 ··· 1602 1602 if (c->pretty_ctx->reflog_info) 1603 1603 get_reflog_selector(sb, 1604 1604 c->pretty_ctx->reflog_info, 1605 - &c->pretty_ctx->date_mode, 1605 + c->pretty_ctx->date_mode, 1606 1606 c->pretty_ctx->date_mode_explicit, 1607 1607 (placeholder[1] == 'd')); 1608 1608 return 2; ··· 1617 1617 return format_reflog_person(sb, 1618 1618 placeholder[1], 1619 1619 c->pretty_ctx->reflog_info, 1620 - &c->pretty_ctx->date_mode); 1620 + c->pretty_ctx->date_mode); 1621 1621 } 1622 1622 return 0; /* unknown %g placeholder */ 1623 1623 case 'N': ··· 1712 1712 case 'a': /* author ... */ 1713 1713 return format_person_part(sb, placeholder[1], 1714 1714 msg + c->author.off, c->author.len, 1715 - &c->pretty_ctx->date_mode); 1715 + c->pretty_ctx->date_mode); 1716 1716 case 'c': /* committer ... */ 1717 1717 return format_person_part(sb, placeholder[1], 1718 1718 msg + c->committer.off, c->committer.len, 1719 - &c->pretty_ctx->date_mode); 1719 + c->pretty_ctx->date_mode); 1720 1720 case 'e': /* encoding */ 1721 1721 if (c->commit_encoding) 1722 1722 strbuf_addstr(sb, c->commit_encoding);
+1 -1
pretty.h
··· 168 168 * a well-known sentinel date if they appear bogus. 169 169 */ 170 170 const char *show_ident_date(const struct ident_split *id, 171 - const struct date_mode *mode); 171 + struct date_mode mode); 172 172 173 173 174 174 #endif /* PRETTY_H */
+1 -1
ref-filter.c
··· 1627 1627 tz = strtol(zone, NULL, 10); 1628 1628 if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) 1629 1629 goto bad; 1630 - v->s = xstrdup(show_date(timestamp, tz, &date_mode)); 1630 + v->s = xstrdup(show_date(timestamp, tz, date_mode)); 1631 1631 v->value = timestamp; 1632 1632 date_mode_release(&date_mode); 1633 1633 return;
+2 -2
reflog-walk.c
··· 223 223 224 224 void get_reflog_selector(struct strbuf *sb, 225 225 struct reflog_walk_info *reflog_info, 226 - const struct date_mode *dmode, int force_date, 226 + struct date_mode dmode, int force_date, 227 227 int shorten) 228 228 { 229 229 struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; ··· 297 297 } 298 298 299 299 void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline, 300 - const struct date_mode *dmode, int force_date) 300 + struct date_mode dmode, int force_date) 301 301 { 302 302 if (reflog_info && reflog_info->last_commit_reflog) { 303 303 struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
+2 -2
reflog-walk.h
··· 10 10 int add_reflog_for_walk(struct reflog_walk_info *info, 11 11 struct commit *commit, const char *name); 12 12 void show_reflog_message(struct reflog_walk_info *info, int, 13 - const struct date_mode *, int force_date); 13 + struct date_mode, int force_date); 14 14 void get_reflog_message(struct strbuf *sb, 15 15 struct reflog_walk_info *reflog_info); 16 16 const char *get_reflog_ident(struct reflog_walk_info *reflog_info); 17 17 timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info); 18 18 void get_reflog_selector(struct strbuf *sb, 19 19 struct reflog_walk_info *reflog_info, 20 - const struct date_mode *dmode, int force_date, 20 + struct date_mode dmode, int force_date, 21 21 int shorten); 22 22 23 23 int reflog_walk_empty(struct reflog_walk_info *walk);
+1 -1
t/helper/test-date.c
··· 52 52 arg++; 53 53 tz = atoi(arg); 54 54 55 - printf("%s -> %s\n", *argv, show_date(t, tz, &mode)); 55 + printf("%s -> %s\n", *argv, show_date(t, tz, mode)); 56 56 } 57 57 58 58 date_mode_release(&mode);