|
|
| version 1.62.2.1.2.2, 2001/12/06 21:34:50 | version 1.71, 2002/02/08 08:30:10 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: parser @b operators. | Parser: parser @b operators. |
| Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com) | Copyright (c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com) |
| Author: Alexander Petrosyan <paf@design.ru> (http://paf.design.ru) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| $Id$ | $Id$ |
| */ | */ |
| #include "classes.h" | #include "classes.h" |
| #include "pa_config_includes.h" | |
| #include "pa_common.h" | #include "pa_common.h" |
| #include "pa_request.h" | #include "pa_request.h" |
| #include "pa_vint.h" | #include "pa_vint.h" |
| Line 125 static void _process(Request& r, const S | Line 124 static void _process(Request& r, const S |
| // process source code, append processed methods to 'self' class | // process source code, append processed methods to 'self' class |
| // maybe-define new @main | // maybe-define new @main |
| r.use_buf(source.cstr(String::UL_UNSPECIFIED, r.connection), place, &self_class); | r.use_buf( |
| source.cstr(String::UL_UNSPECIFIED, r.connection(0)), | |
| place, | |
| &self_class); | |
| // maybe-execute @main[] | // maybe-execute @main[] |
| if(const Method *method=self_class.get_method(*main_method_name)) { | if(const Method *method=self_class.get_method(*main_method_name)) { |
| Line 246 struct timeval mt[2]; | Line 248 struct timeval mt[2]; |
| gettimeofday(&mt[0],NULL); | gettimeofday(&mt[0],NULL); |
| #endif | #endif |
| // connect | // connect |
| SQL_Connection& connection=SQL_driver_manager->get_connection( | SQL_Connection_ptr connection=SQL_driver_manager->get_connection( |
| url.as_string(), method_name, protocol2driver_and_client); | url.as_string(), method_name, protocol2driver_and_client); |
| #ifdef RESOURCES_DEBUG | #ifdef RESOURCES_DEBUG |
| Line 259 for(int i=0;i<2;i++) | Line 261 for(int i=0;i<2;i++) |
| r.sql_connect_time+=t[1]-t[0]; | r.sql_connect_time+=t[1]-t[0]; |
| #endif | #endif |
| // remember/set current connection | Temp_connection temp_connection(r, connection.get()); |
| SQL_Connection *saved_connection=r.connection; | |
| r.connection=&connection; | |
| // execute body | // execute body |
| try { | try { |
| try { | r.write_assign_lang(r.process(body_code)); |
| r.write_assign_lang(r.process(body_code)); | |
| } catch(...) { // process/commit problem | |
| connection.commit(); | connection->mark_to_rollback(); |
| } catch(...) { // process/commit problem | /*re*/throw; |
| connection.rollback(); | |
| /*re*/throw; | |
| } | |
| } catch(...) { | |
| // close connection [cache it] | |
| connection.close(); | |
| // recall current connection from remembered | |
| r.connection=saved_connection; | |
| /*re*/throw; | |
| } | } |
| // and anyway | |
| // close connection [cache it] | |
| connection.close(); | |
| // recall current connection from remembered | |
| r.connection=saved_connection; | |
| } | } |
| #ifndef DOXYGEN | #ifndef DOXYGEN |
| Line 356 struct Data_string_serialized_prolog { | Line 338 struct Data_string_serialized_prolog { |
| }; | }; |
| #endif | #endif |
| void cache_delete(Pool& pool, const String& file_spec) { | void cache_delete(const String& file_spec) { |
| file_delete(pool, file_spec, false/*fail_on_read_problem*/); | file_delete(file_spec, false/*fail_on_read_problem*/); |
| } | } |
| void cache_put(Pool& pool, const String& file_spec, const String& data_string) { | |
| #ifndef DOXYGEN | |
| struct Locked_process_and_cache_put_action_info { | |
| Request *r; | |
| Value *body; | |
| }; | |
| #endif | |
| static void locked_process_and_cache_put_action(int f, void *context) { | |
| Locked_process_and_cache_put_action_info& info= | |
| *static_cast<Locked_process_and_cache_put_action_info *>(context); | |
| // body->process | |
| info.body=&info.r->process(*info.body); | |
| // result->string | |
| const String& data_string=info.body->as_string(); | |
| // string -serialize> buffer | |
| void *data; size_t data_size; | void *data; size_t data_size; |
| data_string.serialize( | data_string.serialize( |
| sizeof(Data_string_serialized_prolog), | sizeof(Data_string_serialized_prolog), |
| data, data_size); | data, data_size); |
| Data_string_serialized_prolog& prolog= | Data_string_serialized_prolog& prolog= |
| *static_cast<Data_string_serialized_prolog *>(data); | *static_cast<Data_string_serialized_prolog *>(data); |
| prolog.version=DATA_STRING_SERIALIZED_VERSION; | prolog.version=DATA_STRING_SERIALIZED_VERSION; |
| file_write(pool, | // buffer -write> file |
| file_spec, | write(f, data, data_size); |
| data, data_size, | } |
| false/*as_text*/); | Value *locked_process_and_cache_put(Request& r, |
| Value& body_code, | |
| const String& file_spec) { | |
| Locked_process_and_cache_put_action_info info={ | |
| &r, | |
| &body_code, | |
| }; | |
| return file_write_action_under_lock( | |
| file_spec, | |
| "cache_put", locked_process_and_cache_put_action, &info, | |
| false/*as_text*/, | |
| false/*do_append*/, | |
| false/*block*/) ? info.body : 0; | |
| } | } |
| String *cache_get(Pool& pool, const String& file_spec) { | String *cache_get(Pool& pool, const String& file_spec) { |
| void* data; size_t data_size; | void* data; size_t data_size; |
| if(!file_read(pool, file_spec, | if(!file_read(pool, file_spec, |
| data, data_size, | data, data_size, |
| false/*as_text*/, | false/*as_text*/, |
| false/*fail_on_read_problem*/)) | false/*fail_on_read_problem*/) |
| || !data_size/* ignore reads which are empty due to | |
| non-unary open+lockEX conflict with lockSH */) | |
| return 0; | return 0; |
| Data_string_serialized_prolog& prolog= | Data_string_serialized_prolog& prolog= |
| Line 388 String *cache_get(Pool& pool, const Stri | Line 401 String *cache_get(Pool& pool, const Stri |
| if(data_size<sizeof(Data_string_serialized_prolog)) | if(data_size<sizeof(Data_string_serialized_prolog)) |
| throw Exception(0, 0, | throw Exception(0, 0, |
| &file_spec, | &file_spec, |
| "bad cached file, too short"); | "bad cached file (size=%d), too short (minsize=%d)", |
| data_size, sizeof(Data_string_serialized_prolog)); | |
| if(prolog.version!=DATA_STRING_SERIALIZED_VERSION) | if(prolog.version!=DATA_STRING_SERIALIZED_VERSION) |
| throw Exception(0, 0, | throw Exception(0, 0, |
| Line 397 String *cache_get(Pool& pool, const Stri | Line 411 String *cache_get(Pool& pool, const Stri |
| prolog.version, DATA_STRING_SERIALIZED_VERSION); | prolog.version, DATA_STRING_SERIALIZED_VERSION); |
| String& result=*new(pool) String(pool); | String& result=*new(pool) String(pool); |
| if(data_size) { | result.deserialize( |
| result.deserialize( | sizeof(Data_string_serialized_prolog), |
| sizeof(Data_string_serialized_prolog), | data, data_size, file_spec.cstr()); |
| data, data_size, file_spec.cstr()); | |
| } | |
| return &result; | return &result; |
| } | } |
| static void _cache(Request& r, const String& method_name, MethodParams *params) { | static void _cache(Request& r, const String& method_name, MethodParams *params) { |
| // ^cache[file_spec](lifespan){code} time=0 no cache | |
| Pool& pool=r.pool(); | Pool& pool=r.pool(); |
| // file_spec, expires, body code | // file_spec, expires, body code |
| const String &file_spec=params->as_string(0, "key must be string"); | const String &file_spec=r.absolute(params->as_string(0, "filespec must be string")); |
| if(params->size()==1) { // delete | |
| cache_delete(file_spec); | |
| return; | |
| } | |
| time_t lifespan=(time_t)params->as_double(1, "lifespan must be number", r); | time_t lifespan=(time_t)params->as_double(1, "lifespan must be number", r); |
| Value& body_code=params->as_junction(2, "body must be code"); | Value& body_code=params->as_junction(2, "body must be code"); |
| if(lifespan) { // 'lifespan' specified? try cached copy... | if(lifespan) { // 'lifespan' specified? try cached copy... |
| size_t size; | size_t size; |
| time_t atime, mtime, ctime; | time_t atime, mtime, ctime; |
| // hence we don't hope to have unary create/lockEX | |
| // we need some plan to live in a life like that, so... | |
| // worst races plan: | |
| // A B | |
| // open | |
| // |open | |
| // lockSH | |
| // |nonblocking-lockEX fails | |
| // unlockSH | |
| // close, cache_get returns 0 | |
| // open | |
| // nonblocking-lockEX succeeds; process, write, close | |
| // |retry1: open | |
| // ... | |
| // |lockSH succeeds; ... | |
| // {file_spec} modification time | // {file_spec} modification time |
| if(!file_stat(file_spec, size, atime, mtime, ctime, false/*no exception on error*/) | for(int retry=0; retry<2; retry++) { |
| || (time(0)-mtime) > lifespan) // cached file expired | if(file_stat(file_spec, size, atime, mtime, ctime, false/*no exception on error*/)) // exists? |
| cache_delete(pool, file_spec); | if(time(0)-mtime > lifespan) // expired |
| else | cache_delete(file_spec); |
| if(String *cached_body=cache_get(pool, file_spec)) { // have cached copy? | else // not expired |
| if(String *cached_body=cache_get(pool, file_spec)) { // have cached copy? | |
| // write it out | |
| r.write_assign_lang(*cached_body); | |
| // happy with it | |
| return; | |
| } | |
| // non-blocked lock; process; cache it | |
| if(Value *processed_body=locked_process_and_cache_put(r, body_code, file_spec)) { | |
| // write it out | // write it out |
| r.write_assign_lang(*cached_body); | r.write_assign_lang(*processed_body); |
| // happy with it | // happy with it |
| return; | return; |
| } else { // somebody writing result right now | |
| pa_sleep(0, 500000); // waiting half a second | |
| retry=0; // prolonging our wait, than could cache_get it, without processing body_code | |
| } | } |
| } else // 'lifespan'=0, forget cached copy | } |
| cache_delete(pool, file_spec); | throw Exception(0, 0, |
| &file_spec, | |
| // process | "locking problem"); |
| Value& processed_body=r.process(body_code); | } else { |
| // 'lifespan'=0, forget cached copy | |
| // put it to cache if 'lifespan' specified | cache_delete(file_spec); |
| if(lifespan) | // process |
| cache_put(pool, file_spec, processed_body.as_string()); | Value& processed_body=r.process(body_code); |
| // write it out | |
| // write it out | r.write_assign_lang(processed_body); |
| r.write_assign_lang(processed_body); | // happy with it |
| return; | |
| } | |
| // never reached | |
| } | } |
| // constructor | // constructor |
| Line 487 MOP::MOP(Pool& apool) : Methoded(apool), | Line 534 MOP::MOP(Pool& apool) : Methoded(apool), |
| add_native_method("connect", Method::CT_ANY, _connect, 2, 2); | add_native_method("connect", Method::CT_ANY, _connect, 2, 2); |
| // ^cache[file_spec] delete cache | |
| // ^cache[file_spec](time){code} time=0 no cache | // ^cache[file_spec](time){code} time=0 no cache |
| add_native_method("cache", Method::CT_ANY, _cache, 3, 3); | add_native_method("cache", Method::CT_ANY, _cache, 1, 3); |
| // switch | // switch |