1 //------------------------------------------------------------------------------
2 // emFileModel.cpp
3 //
4 // Copyright (C) 2005-2008,2014,2016,2018-2019 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20
21 #include <emCore/emFileModel.h>
22
23
24 //==============================================================================
25 //================================ emFileModel =================================
26 //==============================================================================
27
GetFilePath() const28 const emString & emFileModel::GetFilePath() const
29 {
30 return GetName();
31 }
32
33
Update()34 void emFileModel::Update()
35 {
36 switch (State) {
37 case FS_WAITING:
38 if (MemoryNeed>1) {
39 MemoryNeed=1;
40 Signal(FileStateSignal);
41 }
42 break;
43 case FS_LOADED:
44 if (IsOutOfDate()) {
45 ResetData();
46 State=FS_TOO_COSTLY;
47 MemoryNeed=1;
48 FileProgress=0.0;
49 if (ClientList) {
50 State=FS_WAITING;
51 StartPSAgent();
52 }
53 Signal(FileStateSignal);
54 }
55 break;
56 case FS_TOO_COSTLY:
57 if (MemoryNeed>1) {
58 MemoryNeed=1;
59 if (ClientList) {
60 State=FS_WAITING;
61 StartPSAgent();
62 }
63 Signal(FileStateSignal);
64 }
65 break;
66 case FS_LOAD_ERROR:
67 State=FS_TOO_COSTLY;
68 ErrorText.Clear();
69 MemoryNeed=1;
70 if (ClientList) {
71 State=FS_WAITING;
72 StartPSAgent();
73 }
74 Signal(FileStateSignal);
75 break;
76 default:
77 break;
78 }
79 }
80
81
AcquireUpdateSignalModel(emRootContext & rootContext)82 emRef<emSigModel> emFileModel::AcquireUpdateSignalModel(
83 emRootContext & rootContext
84 )
85 {
86 return emSigModel::Acquire(rootContext,"emFileModel::UpdateSignal");
87 }
88
89
SetIgnoreUpdateSignal(bool ignore)90 void emFileModel::SetIgnoreUpdateSignal(bool ignore)
91 {
92 if (ignore) {
93 if (UpdateSignalModel) {
94 RemoveWakeUpSignal(UpdateSignalModel->Sig);
95 UpdateSignalModel=NULL;
96 }
97 }
98 else {
99 if (!UpdateSignalModel) {
100 UpdateSignalModel=AcquireUpdateSignalModel(GetRootContext());
101 AddWakeUpSignal(UpdateSignalModel->Sig);
102 }
103 }
104 }
105
106
Load(bool immediately)107 void emFileModel::Load(bool immediately)
108 {
109 bool stateChanged;
110
111 if (State==FS_WAITING || State==FS_LOADING) {
112 stateChanged=StepLoading();
113 if (immediately) {
114 while (State==FS_LOADING) {
115 if (StepLoading()) stateChanged=true;
116 }
117 }
118 if (UpdateFileProgress()) stateChanged=true;
119 if (stateChanged) Signal(FileStateSignal);
120 if (State==FS_LOADING) WakeUp();
121 }
122 }
123
124
Save(bool immediately)125 void emFileModel::Save(bool immediately)
126 {
127 bool stateChanged;
128
129 if (State==FS_SAVING || State==FS_UNSAVED) {
130 stateChanged=StepSaving();
131 if (immediately) {
132 while (State==FS_SAVING) {
133 if (StepSaving()) stateChanged=true;
134 }
135 }
136 if (UpdateFileProgress()) stateChanged=true;
137 if (stateChanged) Signal(FileStateSignal);
138 if (State==FS_SAVING) WakeUp();
139 }
140 }
141
142
ClearSaveError()143 void emFileModel::ClearSaveError()
144 {
145 if (State==FS_SAVE_ERROR) {
146 State=FS_UNSAVED;
147 ErrorText.Clear();
148 Signal(FileStateSignal);
149 }
150 }
151
152
HardResetFileState()153 void emFileModel::HardResetFileState()
154 {
155 EndPSAgent();
156 switch (State) {
157 case FS_LOADING:
158 QuitLoading();
159 ResetData();
160 break;
161 case FS_SAVING:
162 QuitSaving();
163 ResetData();
164 break;
165 case FS_LOADED:
166 case FS_UNSAVED:
167 case FS_SAVE_ERROR:
168 ResetData();
169 break;
170 default:
171 break;
172 }
173 State=FS_TOO_COSTLY;
174 MemoryNeed=1;
175 FileProgress=0.0;
176 ErrorText.Clear();
177 if (ClientList && MemoryLimit>=MemoryNeed) {
178 State=FS_WAITING;
179 StartPSAgent();
180 }
181 Signal(FileStateSignal);
182 }
183
184
emFileModel(emContext & context,const emString & name)185 emFileModel::emFileModel(
186 emContext & context, const emString & name
187 )
188 : emModel(context,name)
189 {
190 State=FS_TOO_COSTLY;
191 MemoryNeed=1;
192 FileProgress=0.0;
193 FileProgressClock=0;
194 ClientList=NULL;
195 MemoryLimit=0;
196 LastMTime=0;
197 LastCTime=0;
198 LastFSize=0;
199 LastINode=0;
200 PSAgent=NULL;
201 UpdateSignalModel=NULL;
202 SetIgnoreUpdateSignal(false);
203 }
204
205
~emFileModel()206 emFileModel::~emFileModel()
207 {
208 EndPSAgent();
209 }
210
211
Cycle()212 bool emFileModel::Cycle()
213 {
214 bool stateChanged;
215
216 if (UpdateSignalModel && IsSignaled(UpdateSignalModel->Sig)) {
217 Update();
218 }
219
220 switch (State) {
221 case FS_WAITING:
222 if (!PSAgent) StartPSAgent();
223 if (!PSAgent->HasAccess()) return false;
224 if (IsTimeSliceAtEnd()) return true;
225 // no break
226 case FS_LOADING:
227 stateChanged=false;
228 do {
229 if (StepLoading()) stateChanged=true;
230 } while (State==FS_LOADING && !IsTimeSliceAtEnd());
231 if (UpdateFileProgress()) stateChanged=true;
232 if (stateChanged) Signal(FileStateSignal);
233 return State==FS_LOADING;
234 case FS_SAVING:
235 stateChanged=false;
236 do {
237 if (StepSaving()) stateChanged=true;
238 } while (State==FS_SAVING && !IsTimeSliceAtEnd());
239 if (UpdateFileProgress()) stateChanged=true;
240 if (stateChanged) Signal(FileStateSignal);
241 return State==FS_SAVING;
242 default:
243 return false;
244 }
245 }
246
247
SetUnsavedState()248 void emFileModel::SetUnsavedState()
249 {
250 if (State!=FS_UNSAVED) {
251 EndPSAgent();
252 switch (State) {
253 case FS_LOADING:
254 QuitLoading();
255 break;
256 case FS_SAVING:
257 QuitSaving();
258 break;
259 default:
260 break;
261 }
262 State=FS_UNSAVED;
263 FileProgress=0.0;
264 ErrorText.Clear();
265 Signal(FileStateSignal);
266 }
267 }
268
269
TryFetchDate()270 void emFileModel::TryFetchDate()
271 {
272 struct em_stat st;
273
274 if (em_stat(GetFilePath().Get(),&st)!=0) {
275 throw emException(
276 "Failed to get info of \"%s\": %s",
277 GetFilePath().Get(),
278 emGetErrorText(errno).Get()
279 );
280 }
281 LastMTime=st.st_mtime;
282 LastCTime=st.st_ctime;
283 LastFSize=st.st_size;
284 LastINode=st.st_ino;
285 }
286
287
IsOutOfDate()288 bool emFileModel::IsOutOfDate()
289 {
290 struct em_stat st;
291
292 if (em_stat(GetFilePath().Get(),&st)!=0) {
293 return true;
294 }
295 return
296 LastMTime!=st.st_mtime ||
297 LastCTime!=st.st_ctime ||
298 LastFSize!=(emUInt64)st.st_size ||
299 LastINode!=(emUInt64)st.st_ino
300 ;
301 }
302
303
ClientsChanged()304 void emFileModel::ClientsChanged()
305 {
306 emFileModelClient * c;
307 emUInt64 m;
308 double pri;
309
310 for (m=0, c=ClientList; c; c=c->NextInList) {
311 if (m<c->MemoryLimit) m=c->MemoryLimit;
312 }
313 MemoryLimit=m;
314
315 if (PSAgent) {
316 c=ClientList;
317 if (c) {
318 pri=c->Priority;
319 for (c=c->NextInList; c; c=c->NextInList) {
320 if (pri<c->Priority) pri=c->Priority;
321 }
322 PSAgent->SetAccessPriority(pri);
323 }
324 }
325
326 switch (State) {
327 case FS_WAITING:
328 if (!ClientList || MemoryLimit<MemoryNeed) {
329 EndPSAgent();
330 State=FS_TOO_COSTLY;
331 Signal(FileStateSignal);
332 }
333 break;
334 case FS_LOADING:
335 if (!ClientList || MemoryLimit<MemoryNeed) {
336 EndPSAgent();
337 QuitLoading();
338 ResetData();
339 State=FS_TOO_COSTLY;
340 FileProgress=0.0;
341 Signal(FileStateSignal);
342 }
343 break;
344 case FS_LOADED:
345 if (!ClientList || MemoryLimit<MemoryNeed) {
346 ResetData();
347 State=FS_TOO_COSTLY;
348 FileProgress=0.0;
349 Signal(FileStateSignal);
350 }
351 break;
352 case FS_TOO_COSTLY:
353 if (ClientList && MemoryLimit>=MemoryNeed) {
354 State=FS_WAITING;
355 StartPSAgent();
356 Signal(FileStateSignal);
357 }
358 break;
359 default:
360 break;
361 }
362 }
363
364
StepLoading()365 bool emFileModel::StepLoading()
366 {
367 bool ready, stateChanged;
368
369 if (State==FS_LOADING) {
370 try {
371 ready=TryContinueLoading();
372 }
373 catch (const emException & exception) {
374 EndPSAgent();
375 QuitLoading();
376 ResetData();
377 State=FS_LOAD_ERROR;
378 ErrorText=exception.GetText();
379 return true;
380 }
381 stateChanged=false;
382 }
383 else if (State==FS_WAITING) {
384 try {
385 TryFetchDate();
386 }
387 catch (const emException & e) {
388 EndPSAgent();
389 State=FS_LOAD_ERROR;
390 ErrorText=e.GetText();
391 return true;
392 }
393 ResetData();
394 State=FS_LOADING;
395 try {
396 TryStartLoading();
397 }
398 catch (const emException & exception) {
399 EndPSAgent();
400 QuitLoading();
401 ResetData();
402 State=FS_LOAD_ERROR;
403 ErrorText=exception.GetText();
404 return true;
405 }
406 ready=false;
407 stateChanged=true;
408 }
409 else {
410 return false;
411 }
412
413 MemoryNeed=CalcMemoryNeed();
414 if (MemoryNeed<1) MemoryNeed=1;
415 if (!ClientList || MemoryNeed>MemoryLimit) {
416 EndPSAgent();
417 QuitLoading();
418 ResetData();
419 State=FS_TOO_COSTLY;
420 return true;
421 }
422 if (!ready) return stateChanged;
423 EndPSAgent();
424 QuitLoading();
425 State=FS_LOADED;
426 return true;
427 }
428
429
StepSaving()430 bool emFileModel::StepSaving()
431 {
432 bool ready;
433
434 if (State==FS_SAVING) {
435 try {
436 ready=TryContinueSaving();
437 }
438 catch (const emException & exception) {
439 EndPSAgent();
440 QuitSaving();
441 State=FS_SAVE_ERROR;
442 ErrorText=exception.GetText();
443 return true;
444 }
445 if (!ready) return false;
446 }
447 else if (State==FS_UNSAVED) {
448 State=FS_SAVING;
449 ErrorText.Clear();
450 try {
451 TryStartSaving();
452 }
453 catch (const emException & exception) {
454 EndPSAgent();
455 QuitSaving();
456 State=FS_SAVE_ERROR;
457 ErrorText=exception.GetText();
458 return true;
459 }
460 return true;
461 }
462 else {
463 return false;
464 }
465
466 EndPSAgent();
467 QuitSaving();
468 try {
469 TryFetchDate();
470 }
471 catch (const emException & e) {
472 State=FS_SAVE_ERROR;
473 ErrorText=e.GetText();
474 return true;
475 }
476 State=FS_LOADED;
477 MemoryNeed=CalcMemoryNeed();
478 if (MemoryNeed<1) MemoryNeed=1;
479 if (!ClientList || MemoryNeed>MemoryLimit) {
480 ResetData();
481 State=FS_TOO_COSTLY;
482 }
483 return true;
484 }
485
486
UpdateFileProgress()487 bool emFileModel::UpdateFileProgress()
488 {
489 double pg;
490 emUInt64 clk;
491
492 switch (State) {
493 case FS_LOADING:
494 case FS_SAVING:
495 clk=emGetClockMS();
496 if (clk-FileProgressClock>=250) {
497 FileProgressClock=clk;
498 pg=CalcFileProgress();
499 }
500 else {
501 return false;
502 }
503 break;
504 case FS_LOADED:
505 case FS_UNSAVED:
506 pg=100.0;
507 break;
508 default:
509 pg=0.0;
510 break;
511 }
512 if (FileProgress>pg-0.01 && FileProgress<pg+0.01) return false;
513 FileProgress=pg;
514 return true;
515 }
516
517
StartPSAgent()518 void emFileModel::StartPSAgent()
519 {
520 emFileModelClient * c;
521 double pri;
522
523 if (!PSAgent) PSAgent=new PSAgentClass(*this);
524 c=ClientList;
525 if (c) {
526 pri=c->Priority;
527 for (c=c->NextInList; c; c=c->NextInList) {
528 if (pri<c->Priority) pri=c->Priority;
529 }
530 PSAgent->SetAccessPriority(pri);
531 }
532 PSAgent->RequestAccess();
533 }
534
535
EndPSAgent()536 void emFileModel::EndPSAgent()
537 {
538 if (PSAgent) {
539 delete PSAgent;
540 PSAgent=NULL;
541 }
542 }
543
544
PSAgentClass(emFileModel & fileModel)545 emFileModel::PSAgentClass::PSAgentClass(
546 emFileModel & fileModel
547 ) :
548 emPriSchedAgent(fileModel.GetRootContext(),"cpu"),
549 FileModel(fileModel)
550 {
551 }
552
553
GotAccess()554 void emFileModel::PSAgentClass::GotAccess()
555 {
556 FileModel.WakeUp();
557 }
558
559
560 //==============================================================================
561 //============================= emFileModelClient ==============================
562 //==============================================================================
563
emFileModelClient(emFileModel * model,emUInt64 memoryLimit,double priority)564 emFileModelClient::emFileModelClient(
565 emFileModel * model, emUInt64 memoryLimit, double priority
566 )
567 {
568 MemoryLimit=memoryLimit;
569 Priority=priority;
570 ThisPtrInList=NULL;
571 NextInList=NULL;
572 if (model) SetModel(model);
573 }
574
575
~emFileModelClient()576 emFileModelClient::~emFileModelClient()
577 {
578 SetModel(NULL);
579 }
580
581
SetModel(emFileModel * model)582 void emFileModelClient::SetModel(emFileModel * model)
583 {
584 if (Model==model) return;
585 if (Model) {
586 *ThisPtrInList=NextInList;
587 if (NextInList) NextInList->ThisPtrInList=ThisPtrInList;
588 ThisPtrInList=NULL;
589 NextInList=NULL;
590 Model->ClientsChanged();
591 Model=NULL;
592 }
593 if (model) {
594 Model=model;
595 NextInList=Model->ClientList;
596 if (NextInList) NextInList->ThisPtrInList=&NextInList;
597 Model->ClientList=this;
598 ThisPtrInList=&Model->ClientList;
599 Model->ClientsChanged();
600 }
601 }
602
603
SetMemoryLimit(emUInt64 bytes)604 void emFileModelClient::SetMemoryLimit(emUInt64 bytes)
605 {
606 if (MemoryLimit!=bytes) {
607 MemoryLimit=bytes;
608 if (Model) Model->ClientsChanged();
609 }
610 }
611
612
SetPriority(double priority)613 void emFileModelClient::SetPriority(double priority)
614 {
615 if (Priority!=priority) {
616 Priority=priority;
617 if (Model) Model->ClientsChanged();
618 }
619 }
620