APUE 线程 - 程序清单





程序清单11-1 打印线程ID

  1. #include "util.h"
  2. #include<pthread.h>
  3.  
  4. pthread_t ntid;
  5.  
  6. void
  7. printids(const char *s)
  8. {
  9. pid_t pid;
  10. pthread_t tid;
  11.  
  12. pid = getpid();
  13. tid = pthread_self();
  14. //之所以打印16进制,便于pthread_t是结构体的话看地址;
  15. printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
  16. (unsigned int)tid, (unsigned int)tid);
  17. }
  18.  
  19. void *
  20. thr_fn(void *arg)
  21. {
  22. printids("new thread: ");
  23. return((void *)0);
  24. }
  25.  
  26. int
  27. main(void)
  28. {
  29. int err;
  30.  
  31. err = pthread_create(&ntid, NULL, thr_fn, NULL);
  32. if (err != 0)
  33. err_quit("can't create thread: %s\n", strerror(err));
  34. printids("main thread:");
  35. sleep(1);//确保会运行新线程
  36. exit(0);
  37. }

程序清单11-2  获得线程退出状态

  1. #include "util.h"
  2. #include <pthread.h>
  3.  
  4. void *
  5. thr_fn1(void *arg)
  6. {
  7. printf("thread 1 returning\n");
  8. return((void *)1);
  9. }
  10.  
  11. void *
  12. thr_fn2(void *arg)
  13. {
  14. printf("thread 2 exiting\n");
  15. pthread_exit((void *)2);
  16. }
  17.  
  18. int
  19. main(void)
  20. {
  21. int err;
  22. pthread_t tid1, tid2;
  23. void *tret;
  24.  
  25. err = pthread_create(&tid1, NULL, thr_fn1, NULL);
  26. if (err != 0)
  27. err_quit("can't create thread 1: %s\n", strerror(err));
  28. err = pthread_create(&tid2, NULL, thr_fn2, NULL);
  29. if (err != 0)
  30. err_quit("can't create thread 2: %s\n", strerror(err));
  31. err = pthread_join(tid1, &tret);
  32. if (err != 0)
  33. err_quit("can't join with thread 1: %s\n", strerror(err));
  34. printf("thread 1 exit code %d\n", (int)tret);
  35. err = pthread_join(tid2, &tret);
  36. if (err != 0)
  37. err_quit("can't join with thread 2: %s\n", strerror(err));
  38. printf("thread 2 exit code %d\n", (int)tret);
  39. exit(0);
  40. }

程序清单11-3  pthread_exit 的參数不对使用

  1. #include "util.h"
  2. #include <pthread.h>
  3.  
  4. struct foo {
  5. int a, b, c, d;
  6. };
  7.  
  8. void
  9. printfoo(const char *s, const struct foo *fp)
  10. {
  11. printf(s);
  12. printf(" structure at 0x%x\n", (unsigned)fp);
  13. printf(" foo.a = %d\n", fp->a);
  14. printf(" foo.b = %d\n", fp->b);
  15. printf(" foo.c = %d\n", fp->c);
  16. printf(" foo.d = %d\n", fp->d);
  17. }
  18.  
  19. void *
  20. thr_fn1(void *arg)
  21. {
  22. struct foo foo = {1, 2, 3, 4};
  23.  
  24. printfoo("thread 1:\n", &foo);
  25. pthread_exit((void *)&foo);
  26. //这里是自己主动变量。退出的时候仅仅是告知监听者退出状态码所在的地址。可是里面的内容在函数退出时就变了;
  27. }
  28.  
  29. void *
  30. thr_fn2(void *arg)
  31. {
  32. printf("thread 2: ID is %ld\n", pthread_self());//这里最好用长整型;
  33. pthread_exit((void *)0);
  34. }
  35.  
  36. int
  37. main(void)
  38. {
  39. int err;
  40. pthread_t tid1, tid2;
  41. struct foo *fp;
  42.  
  43. err = pthread_create(&tid1, NULL, thr_fn1, NULL);
  44. if (err != 0)
  45. err_quit("can't create thread 1: %s\n", strerror(err));
  46. err = pthread_join(tid1, (void *)&fp);
  47. if (err != 0)
  48. err_quit("can't join with thread 1: %s\n", strerror(err));
  49. sleep(1);
  50. printf("parent starting second thread\n");
  51. err = pthread_create(&tid2, NULL, thr_fn2, NULL);
  52. if (err != 0)
  53. err_quit("can't create thread 2: %s\n", strerror(err));
  54. sleep(1);
  55. printfoo("parent:\n", fp);
  56. exit(0);
  57. }

程序清单11-4  线程清理处理程序

  1. #include "util.h"
  2. #include <pthread.h>
  3.  
  4. void
  5. cleanup(void *arg)
  6. {
  7. printf("cleanup: %s\n", (char *)arg);
  8. }
  9.  
  10. void *
  11. thr_fn1(void *arg)
  12. {
  13. printf("thread 1 start\n");
  14. pthread_cleanup_push(cleanup, "thread 1 first handler");
  15. pthread_cleanup_push(cleanup, "thread 1 second handler");
  16. printf("thread 1 push complete\n");
  17. if (arg)
  18. return((void *)1);
  19. //假设从启动例程中返回而终止不会调用清理函数。
  20. pthread_cleanup_pop(0);
  21. pthread_cleanup_pop(0);
  22. return((void *)1);
  23. }
  24.  
  25. void *
  26. thr_fn2(void *arg)
  27. {
  28. printf("thread 2 start\n");
  29. pthread_cleanup_push(cleanup, "thread 2 first handler");
  30. pthread_cleanup_push(cleanup, "thread 2 second handler");
  31. printf("thread 2 push complete\n");
  32. if (arg)
  33. pthread_exit((void *)2);
  34. pthread_cleanup_pop(0);
  35. pthread_cleanup_pop(0);
  36. pthread_exit((void *)2);
  37. }
  38.  
  39. int
  40. main(void)
  41. {
  42. int err;
  43. pthread_t tid1, tid2;
  44. void *tret;
  45.  
  46. err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);
  47. if (err != 0)
  48. err_quit("can't create thread 1: %s\n", strerror(err));
  49. err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);
  50. if (err != 0)
  51. err_quit("can't create thread 2: %s\n", strerror(err));
  52. err = pthread_join(tid1, &tret);
  53. if (err != 0)
  54. err_quit("can't join with thread 1: %s\n", strerror(err));
  55. printf("thread 1 exit code %d\n", (int)tret);
  56. err = pthread_join(tid2, &tret);
  57. if (err != 0)
  58. err_quit("can't join with thread 2: %s\n", strerror(err));
  59. printf("thread 2 exit code %d\n", (int)tret);
  60. exit(0);
  61. }

程序清单11-5  使用相互排斥量保护数据结构

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <pthread.h>
  4.  
  5. struct foo {
  6. int f_count;
  7. pthread_mutex_t f_lock;
  8. /* ... more stuff here ... */
  9. };
  10.  
  11. struct foo *
  12. foo_alloc(void) /* allocate the object */
  13. {
  14. struct foo *fp;
  15.  
  16. if ((fp = malloc(sizeof(struct foo))) != NULL) {
  17. fp->f_count = 1;
  18. if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
  19. free(fp);
  20. return(NULL);
  21. }
  22. /* ... continue initialization ... */
  23. }
  24. return(fp);
  25. }
  26.  
  27. void
  28. foo_hold(struct foo *fp) /* add a reference to the object */
  29. {
  30. pthread_mutex_lock(&fp->f_lock);
  31. fp->f_count++;
  32. pthread_mutex_unlock(&fp->f_lock);
  33. }
  34.  
  35. void
  36. foo_rele(struct foo *fp) /* release a reference to the object */
  37. {
  38. pthread_mutex_lock(&fp->f_lock);
  39. if (--fp->f_count == 0) { /* last reference */
  40. pthread_mutex_unlock(&fp->f_lock);
  41. pthread_mutex_destroy(&fp->f_lock);
  42. free(fp);
  43. } else {
  44. pthread_mutex_unlock(&fp->f_lock);
  45. }
  46. }
  47.  
  48. void *thr_fn1(void *pp){
  49. struct foo *p=(struct foo *)pp;
  50. printf("thread 1.......\n");
  51. foo_hold(p);
  52. pthread_exit((void*)1);
  53. }
  54. void *thr_fn2(void *pp){
  55. struct foo *p=(struct foo *)pp;
  56. printf("thread 2.......\n");
  57. foo_hold(p);
  58. pthread_exit((void*)2);
  59. }
  60. int
  61. main(){
  62. pthread_t tid1,tid2;
  63. void * ret1,*ret2;
  64. struct foo *pf;
  65. pf = foo_alloc();
  66. if(!pf)
  67. exit(-1);
  68. pthread_create(&tid1,NULL,thr_fn1,(void *)pf);
  69.  
  70. pthread_join(tid1,&ret1);
  71. printf("main 1 : %d --\n",pf->f_count);
  72. pthread_create(&tid2,NULL,thr_fn2,(void *)pf);
  73. printf("main 2 : %d --\n",pf->f_count);
  74.  
  75. pthread_join(tid2,&ret2);
  76. printf("main 3 : %d --\n",pf->f_count);
  77.  
  78. }

程序清单11-6  使用两个相互排斥量

  1. #include <stdlib.h>
  2. #include <pthread.h>
  3.  
  4. #define NHASH 29
  5. #define HASH(fp) (((unsigned long)fp)%NHASH)
  6.  
  7. struct foo *fh[NHASH];
  8.  
  9. pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
  10.  
  11. struct foo {
  12. int f_count;
  13. pthread_mutex_t f_lock;
  14. struct foo *f_next; /* protected by hashlock */
  15. int f_id;
  16. /* ... more stuff here ... */
  17. };
  18.  
  19. struct foo *
  20. foo_alloc(void) /* allocate the object */
  21. {
  22. struct foo *fp;
  23. int idx;
  24. if ((fp = malloc(sizeof(struct foo))) != NULL) {
  25. fp->f_count = 1;
  26. if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
  27. free(fp);
  28. return(NULL);
  29. }
  30. idx = HASH(fp);
  31. pthread_mutex_lock(&hashlock);
  32. fp->f_next = fh[idx];
  33. fh[idx] = fp->f_next;
  34. pthread_mutex_lock(&fp->f_lock);
  35. pthread_mutex_unlock(&hashlock);
  36. /* ... continue initialization ... */
  37. thread_mutex_unlock(&fp->f_lock);
  38. }
  39. return(fp);
  40. }
  41.  
  42. void
  43. foo_hold(struct foo *fp) /* add a reference to the object */
  44. {
  45. pthread_mutex_lock(&fp->f_lock);
  46. fp->f_count++;
  47. pthread_mutex_unlock(&fp->f_lock);
  48. }
  49.  
  50. struct foo *
  51. foo_find(int id) /* find an existing object */
  52. {
  53. struct foo *fp;
  54. int idx;
  55.  
  56. idx = HASH(fp);
  57. pthread_mutex_lock(&hashlock);
  58. for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
  59. if (fp->f_id == id) {
  60. foo_hold(fp);
  61. break;
  62. }
  63. }
  64. pthread_mutex_unlock(&hashlock);
  65. return(fp);
  66. }
  67.  
  68. void
  69. foo_rele(struct foo *fp) /* release a reference to the object */
  70. {
  71. struct foo *tfp;
  72. int idx;
  73.  
  74. pthread_mutex_lock(&fp->f_lock);
  75. if (fp->f_count == 1) { /* last reference */
  76. pthread_mutex_unlock(&fp->f_lock);
  77. pthread_mutex_lock(&hashlock);
  78. pthread_mutex_lock(&fp->f_lock);
  79. /* need to recheck the condition */
  80. if (fp->f_count != 1) {
  81. fp->f_count--;
  82. pthread_mutex_unlock(&fp->f_lock);
  83. pthread_mutex_unlock(&hashlock);
  84. return;
  85. }
  86. /* remove from list */
  87. idx = HASH(fp);
  88. tfp = fh[idx];
  89. if (tfp == fp) {
  90. fh[idx] = fp->f_next;
  91. } else {
  92. while (tfp->f_next != fp)
  93. tfp = tfp->f_next;
  94. tfp->f_next = fp->f_next;
  95. }
  96. pthread_mutex_unlock(&hashlock);
  97. pthread_mutex_unlock(&fp->f_lock);
  98. pthread_mutex_destroy(&fp->f_lock);
  99. free(fp);
  100. } else {
  101. fp->f_count--;
  102. pthread_mutex_unlock(&fp->f_lock);
  103. }
  104. }

程序清单11-7  简化的加,解锁

  1. #include <stdlib.h>
  2. #include <pthread.h>
  3.  
  4. #define NHASH 29
  5. #define HASH(fp) (((unsigned long)fp)%NHASH)
  6.  
  7. struct foo *fh[NHASH];
  8. pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
  9.  
  10. struct foo {
  11. int f_count; /* protected by hashlock */
  12. pthread_mutex_t f_lock;
  13. struct foo *f_next; /* protected by hashlock */
  14. int f_id;
  15. /* ... more stuff here ... */
  16. };
  17.  
  18. struct foo *
  19. foo_alloc(void) /* allocate the object */
  20. {
  21. struct foo *fp;
  22. int idx;
  23.  
  24. if ((fp = malloc(sizeof(struct foo))) != NULL) {
  25. fp->f_count = 1;
  26. if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
  27. free(fp);
  28. return NULL;
  29. }
  30. idx = HASH(fp);
  31. pthread_mutex_lock(&hashlock);
  32. fp->f_next = fh[idx];
  33. fh[idx] = fp;
  34. pthread_mutex_lock(&fp->f_lock); // Why ?
  35.  
  36. ??
  37. pthread_mutex_unlock(&hashlock);
  38. /* ... continue initialization ... */
  39. }
  40. return fp;
  41. }
  42.  
  43. void
  44. foo_hold(struct foo *fp) /* add a reference to the object */
  45. {
  46. pthread_mutex_lock(&hashlock);
  47. fp->f_count++;
  48. pthread_mutex_unlock(&hashlock);
  49. }
  50.  
  51. struct foo *
  52. foo_find(int id) /* find an existing object */
  53. {
  54. struct foo *fp;
  55. int idx;
  56.  
  57. idx = HASH(fp);
  58. pthread_mutex_lock(&hashlock);
  59. for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
  60. if (fp->f_id == id) {
  61. fp->f_count++;
  62. break;
  63. }
  64. }
  65. pthread_mutex_unlock(&hashlock);
  66. return fp;
  67. }
  68.  
  69. void
  70. foo_rele(struct foo *fp) /* release a reference to the object */
  71. {
  72. struct foo *tfp;
  73. int idx;
  74.  
  75. pthread_mutex_lock(&hashlock);
  76. if (--fp->f_count == 0) { /* last reference, remove from list */
  77. idx = HASH(fp);
  78. tfp = fh[idx];
  79. if (tfp == fp) {
  80. fh[idx] = fp->f_next;
  81. } else {
  82. while (tfp->f_next != fp)
  83. tfp = tfp->f_next;
  84. tfp->f_next = fp->f_next;
  85. }
  86. pthread_mutex_unlock(&hashlock);
  87. pthread_mutex_destroy(&fp->f_lock);
  88. free(fp);
  89. } else {
  90. pthread_mutex_unlock(&hashlock);
  91. }
  92. }

程序清单11-8  使用读写锁

  1. #include <stdlib.h>
  2. #include <pthread.h>
  3.  
  4. struct job {
  5. struct job *j_next;
  6. struct job *j_prev;
  7. pthread_t j_id; /* tells which thread handles this job */
  8. /* ... more stuff here ... */
  9. };
  10.  
  11. struct queue {
  12. struct job *q_head;
  13. struct job *q_tail;
  14. pthread_rwlock_t q_lock;
  15. };
  16.  
  17. /*
  18. * Initialize a queue.
  19. */
  20. int
  21. queue_init(struct queue *qp)
  22. {
  23. int err;
  24.  
  25. qp->q_head = NULL;
  26. qp->q_tail = NULL;
  27. err = pthread_rwlock_init(&qp->q_lock, NULL);
  28. if (err != 0)
  29. return err;
  30.  
  31. /* ... continue initialization ... */
  32.  
  33. return 0;
  34. }
  35.  
  36. /*
  37. * Insert a job at the head of the queue.
  38. */
  39. void
  40. job_insert(struct queue *qp, struct job *jp)
  41. {
  42. pthread_rwlock_wrlock(&qp->q_lock);
  43. jp->j_next = qp->q_head;
  44. jp->j_prev = NULL;
  45. if (qp->q_head != NULL)
  46. qp->q_head->j_prev = jp;
  47. else
  48. qp->q_tail = jp; /* list was empty */
  49. qp->q_head = jp;
  50. pthread_rwlock_unlock(&qp->q_lock);
  51. }
  52.  
  53. /*
  54. * Append a job on the tail of the queue.
  55. */
  56. void
  57. job_append(struct queue *qp, struct job *jp)
  58. {
  59. pthread_rwlock_wrlock(&qp->q_lock);
  60. jp->j_next = NULL;
  61. jp->j_prev = qp->q_tail;
  62. if (qp->q_tail != NULL)
  63. qp->q_tail->j_next = jp;
  64. else
  65. qp->q_head = jp; /* list was empty */
  66. qp->q_tail = jp;
  67. pthread_rwlock_unlock(&qp->q_lock);
  68. }
  69.  
  70. /*
  71. * Remove the given job from a queue.
  72. */
  73. void
  74. job_remove(struct queue *qp, struct job *jp)
  75. {
  76. pthread_rwlock_wrlock(&qp->q_lock);
  77. if (jp == qp->q_head) {
  78. qp->q_head = jp->j_next;
  79. if (qp->q_tail == jp)
  80. qp->q_tail = NULL;
  81. } else if (jp == qp->q_tail) {
  82. qp->q_tail = jp->j_prev;
  83. if (qp->q_head == jp)
  84. qp->q_head = NULL;
  85. } else {
  86. jp->j_prev->j_next = jp->j_next;
  87. jp->j_next->j_prev = jp->j_prev;
  88. }
  89. pthread_rwlock_unlock(&qp->q_lock);
  90. }
  91.  
  92. /*
  93. * Find a job for the given thread ID.
  94. */
  95. struct job *
  96. job_find(struct queue *qp, pthread_t id)
  97. {
  98. struct job *jp;
  99.  
  100. if (pthread_rwlock_rdlock(&qp->q_lock) != 0)
  101. return NULL;
  102.  
  103. for (jp = qp->q_head; jp != NULL; jp = jp->j_next)
  104. if (pthread_equal(jp->j_id, id))
  105. break;
  106.  
  107. pthread_rwlock_unlock(&qp->q_lock);
  108. return jp;
  109. }

程序清单11-9  使用条件变量

  1. #include <pthread.h>
  2.  
  3. struct msg {
  4. struct msg *m_next;
  5. /* ... more stuff ... */
  6. };
  7.  
  8. struct msg *workq;
  9. pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
  10. pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
  11.  
  12. void
  13. process_msg(void)
  14. {
  15. struct msg *mp;
  16.  
  17. for (;;) {
  18. pthread_mutex_lock(&qlock);
  19. while (workq == NULL)
  20. pthread_cond_wait(&qready, &qlock);
  21. mp = workq;
  22. workq = mp->m_next;
  23. pthread_mutex_unlock(&qlock);
  24. /* now process the message mp */
  25. }
  26. }
  27.  
  28. void
  29. enqueue_msg(struct msg *mp)
  30. {
  31. pthread_mutex_lock(&qlock);
  32. mp->m_next = workq;
  33. workq = mp;
  34. pthread_mutex_unlock(&qlock);
  35. pthread_cond_signal(&qready);
  36. }

APUE 线程 - 程序清单的更多相关文章

  1. APUE信号-程序汇总

    APUE信号-程序汇总      近期重看APUE,发现对于非常多程序的要领还是没有全然理解.所以梳理下便于查看,并且有非常多值得思考的问题. 程序清单10- 1  捕获 SIGUSR1 和 SIGU ...

  2. 程序清单 8-8 exec函数实例,a.out是程序8-9产生的可执行程序

    /* ============================================================================ Name : test.c Author ...

  3. 程序清单8-3 8-4 演示不同的exit值

    //http://blog.chinaunix.net/uid-24549279-id-71355.html /* ========================================== ...

  4. windows phone 8 开发系列(三)程序清单说明与配置

    一 清单文件内容介绍 当我们先建了一个项目之后,我们可以看到vs自动会为我们创建了很多文件,正常人都会先一个个去翻看下每个文件都是干啥的,都主要写了些啥,在这些文件中,在Properies目录下面,我 ...

  5. [C++ Primer Plus] 第11章、使用类(一)程序清单——重载 P408

    程序清单11.4~11.6(运算符重载——添加加法运算符) //1.h class Time { private: int hours; int minutes; public: Time(); Ti ...

  6. [C++ Primer Plus] 第10章、对象和类(一)程序清单——辨析三个const

    程序清单10.1+10.2+10.3 头文件stock.h #ifndef STOCK00_H_ //先测试x是否被宏定义过 #define STOCK00_H_ //如果没有宏定义,就宏定义x并编译 ...

  7. [C++ Primer Plus] 第9章、内存模型和名称空间(一)程序清单

    程序清单9.9(静态存储连续性.无链接性) #include<iostream> using namespace std; ; void strcount(const char *str) ...

  8. [C++ Primer Plus] 第8章、函数探幽(一)程序清单——内联、引用、格式化输入输出、模板、decltype

    程序清单8.1(inline内联函数) #include<iostream> using namespace std; inline double square(double x) {// ...

  9. [C++ Primer Plus] 第7章、函数(一)程序清单——递归,指针和const,指针数组和数组指针,函数和二维数组

    程序清单7.6 #include<iostream> using namespace std; ; int sum_arr(int arr[], int n);//函数声明 void ma ...

随机推荐

  1. 第4节 hive调优:动态分区调整问题

    执行如下截图中的语句时卡住了: 原因:yarn未启动,hive底层是要提交mapreduce到yarn上才能计算结果的. 之前启动yarn时,未执行jps查看是否已经启动.其实未启动成功: [root ...

  2. Maven实战读书笔记(二):Maven坐标与仓库

    2.1 Maven坐标 Maven坐标是Maven用来表示一个组件依赖的标示. Maven通过下面几个元素定义坐标:groupId.artifactId.version.packaging.class ...

  3. ICPC-Beijing 2006 狼抓兔子

    题目描述 题解: 裸的最小割. 但是最大流跑不过去怎么办? 转变一下,既然最大流是一条左下<->右上的通路,我们可以把图划分为若干区域, 最后找左下到右上的最短路就行了. 代码: #inc ...

  4. MySQL redo log 与 binlog 的区别

    MySQL redo log 与 binlog 的区别 什么是redo log 什么是binlog redo log与binlog的区别 1. 什么是redo log? redo log又称重做日志文 ...

  5. InnoDB体系架构总结(一)

    缓冲池:    是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响.在数据库中读取的页数据会存放到缓冲池中,下次再读取相同页的时候,会首先判断该页是否在缓冲池中.对于数据库中页的修改操 ...

  6. 官网Android离线文档下载

    这是Android的离线API及一些Guide——俗称的/docs文件夹下的内容——英文版的...——http://pan.baidu.com/s/1qXmLlQc

  7. POJ 2251-Dungeon Master (三维空间求最短路径)

    Description You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is co ...

  8. fstream,sstream的学习记录

    fstream: #include<iostream> #include<fstream> using namespace std; int main(){ ofstream ...

  9. You Are the One (区间DP)

    The TV shows such as You Are the One has been very popular. In order to meet the need of boys who ar ...

  10. POJ-3468A Simple Problem with Integers,线段数区间更新查询,代码打了无数次还是会出错~~

    A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K                Case Time L ...