1 /*  $Id: time_man.cpp 530755 2017-03-17 13:19:30Z gouriano $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Pavel Ivanov
27  *
28  */
29 
30 #include "task_server_pch.hpp"
31 
32 #include <corelib/ncbireg.hpp>
33 
34 #include "time_man.hpp"
35 
36 
37 BEGIN_NCBI_SCOPE;
38 
39 
40 class CTZAdjuster : public CSrvTask
41 {
42 public:
43     CTZAdjuster(void);
44     virtual ~CTZAdjuster(void);
45 
46 private:
47     virtual void ExecuteSlice(TSrvThreadNum thr_num);
48 };
49 
50 
51 CSrvTime s_SrvStartTime;
52 CSrvTime s_LastJiffyTime;
53 Uint8 s_CurJiffies = 0;
54 CSrvTime s_JiffyTime;
55 static time_t s_TZAdjustment = 0;
56 static CTZAdjuster* s_Adjuster = NULL;
57 
58 
59 static Uint4
s_InitTZAdjustment(void)60 s_InitTZAdjustment(void)
61 {
62 #ifdef NCBI_OS_LINUX
63     CSrvTime srv_t = CSrvTime::Current();
64     struct tm t;
65     gmtime_r(&srv_t.Sec(), &t);
66     t.tm_isdst = -1;
67     time_t loc_time = mktime(&t);
68     s_TZAdjustment = srv_t.Sec() - loc_time;
69 
70     // Return number of seconds until it's 1 second after the hour boundary
71     return (60 - t.tm_min) * 60 - (t.tm_sec - 1);
72 #else
73     return 0;
74 #endif
75 }
76 
77 void
InitTime(void)78 InitTime(void)
79 {
80     s_LastJiffyTime = s_SrvStartTime = CSrvTime::Current();
81     s_InitTZAdjustment();
82 }
83 
84 void
InitTimeMan(void)85 InitTimeMan(void)
86 {
87     s_Adjuster = new CTZAdjuster();
88     s_Adjuster->SetRunnable();
89 }
90 
91 void
ConfigureTimeMan(const CNcbiRegistry * reg,CTempString section)92 ConfigureTimeMan(const CNcbiRegistry* reg, CTempString section)
93 {
94     Uint4 clock_freq = Uint4(reg->GetInt(section, "jiffies_per_sec", 100));
95     s_JiffyTime.NSec() = kUSecsPerSecond * kNSecsPerUSec / clock_freq;
96 }
97 
ReConfig_TimeMan(const CTempString &,const CNcbiRegistry &,string &)98 bool ReConfig_TimeMan(const CTempString&, const CNcbiRegistry&, string&)
99 {
100     return true;
101 }
102 
WriteSetup_TimeMan(CSrvSocketTask & task)103 void WriteSetup_TimeMan(CSrvSocketTask& task)
104 {
105     string is("\": "), eol(",\n\"");
106     task.WriteText(eol).WriteText("jiffies_per_sec").WriteText(is ).WriteNumber( kUSecsPerSecond * kNSecsPerUSec / s_JiffyTime.NSec());
107 }
108 
109 void
IncCurJiffies(void)110 IncCurJiffies(void)
111 {
112     s_LastJiffyTime = CSrvTime::Current();
113     ++s_CurJiffies;
114 }
115 
116 static inline void
s_Print1Dig(char * & buf,int num)117 s_Print1Dig(char*& buf, int num)
118 {
119     *(buf++) = char(num + '0');
120 }
121 
122 static inline void
s_Print2Digs(char * & buf,int num)123 s_Print2Digs(char*& buf, int num)
124 {
125     int hi = num / 10;
126     s_Print1Dig(buf, hi);
127     s_Print1Dig(buf, num - hi * 10);
128 }
129 
130 static inline void
s_Print3Digs(char * & buf,int num)131 s_Print3Digs(char*& buf, int num)
132 {
133     int hi = num / 100;
134     s_Print1Dig(buf, hi);
135     s_Print2Digs(buf, num - hi * 100);
136 }
137 
138 static inline void
s_Print4Digs(char * & buf,int num)139 s_Print4Digs(char*& buf, int num)
140 {
141     int hi = num / 100;
142     s_Print2Digs(buf, hi);
143     s_Print2Digs(buf, num - hi * 100);
144 }
145 
146 static inline void
s_Print6Digs(char * & buf,int num)147 s_Print6Digs(char*& buf, int num)
148 {
149     int hi = num / 100;
150     s_Print4Digs(buf, hi);
151     s_Print2Digs(buf, num - hi * 100);
152 }
153 
154 Uint1
Print(char * buf,EFormatType fmt) const155 CSrvTime::Print(char* buf, EFormatType fmt) const
156 {
157     char* start_buf = buf;
158 
159 #ifdef NCBI_OS_LINUX
160     time_t sec = tv_sec + s_TZAdjustment;
161     struct tm t;
162     gmtime_r(&sec, &t);
163 
164     switch (fmt) {
165     default:
166         SRV_LOG(Error, "Unsupported time format: " << fmt);
167         // no break
168     case eFmtLogging:
169         s_Print4Digs(buf, t.tm_year + 1900);
170         *(buf++) = '-';
171         s_Print2Digs(buf, t.tm_mon + 1);
172         *(buf++) = '-';
173         s_Print2Digs(buf, t.tm_mday);
174         *(buf++) = 'T';
175         s_Print2Digs(buf, t.tm_hour);
176         *(buf++) = ':';
177         s_Print2Digs(buf, t.tm_min);
178         *(buf++) = ':';
179         s_Print2Digs(buf, t.tm_sec);
180         *(buf++) = '.';
181         s_Print6Digs(buf, int(tv_nsec / kNSecsPerUSec));
182         break;
183     case eFmtJson:
184         if ( tv_sec == 0 && tv_nsec == 0) {
185             *(buf++) = 'n';
186             *(buf++) = 'u';
187             *(buf++) = 'l';
188             *(buf++) = 'l';
189             break;
190         } else {
191             *(buf++) = '\"';
192         }
193     case eFmtHumanSeconds:
194     case eFmtHumanUSecs:
195         s_Print2Digs(buf, t.tm_mon + 1);
196         *(buf++) = '/';
197         s_Print2Digs(buf, t.tm_mday);
198         *(buf++) = '/';
199         s_Print4Digs(buf, t.tm_year + 1900);
200         *(buf++) = ' ';
201         s_Print2Digs(buf, t.tm_hour);
202         *(buf++) = ':';
203         s_Print2Digs(buf, t.tm_min);
204         *(buf++) = ':';
205         s_Print2Digs(buf, t.tm_sec);
206         if (fmt == eFmtHumanUSecs) {
207             *(buf++) = '.';
208             s_Print6Digs(buf, int(tv_nsec / kNSecsPerUSec));
209         }
210         if (fmt == eFmtJson) {
211             *(buf++) = '\"';
212         }
213         break;
214     }
215 #endif
216 
217     *buf = '\0';
218     return Uint1(buf - start_buf);
219 }
220 
221 int
TZAdjustment(void)222 CSrvTime::TZAdjustment(void)
223 {
224     return int(s_TZAdjustment);
225 }
226 
227 
CTZAdjuster(void)228 CTZAdjuster::CTZAdjuster(void)
229 {
230 #if __NC_TASKS_MONITOR
231     m_TaskName = "CTZAdjuster";
232 #endif
233 }
234 
~CTZAdjuster(void)235 CTZAdjuster::~CTZAdjuster(void)
236 {}
237 
238 void
ExecuteSlice(TSrvThreadNum)239 CTZAdjuster::ExecuteSlice(TSrvThreadNum /* thr_num */)
240 {
241 // on one second after an hour, calculates difference with GMT time
242     Uint4 delay = s_InitTZAdjustment();
243     if (!CTaskServer::IsInShutdown())
244         RunAfter(delay);
245 }
246 
247 END_NCBI_SCOPE;
248