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