Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_accdb_fd_accdb_sync_h
2 : #define HEADER_fd_src_flamenco_accdb_fd_accdb_sync_h
3 :
4 : /* fd_accdb_sync.h provides synchronous blocking APIs for the account
5 : database. These are slow and should only be used for management ops. */
6 :
7 : #include "fd_accdb_user.h"
8 :
9 : /* Speculative zero-copy read API *************************************/
10 :
11 : FD_PROTOTYPES_BEGIN
12 :
13 : /* fd_accdb_peek_try starts a speculative read of an account. Queries
14 : the account database cache for the given address. On success,
15 : returns peek, which holds a speculative reference to an account. Use
16 : fd_accdb_peek_test to confirm whether peek is still valid.
17 :
18 : Typical usage like:
19 :
20 : fd_accdb_peek_t peek[1];
21 : if( !fd_accdb_peek( accdb, ... ) ) {
22 : ... account not found ...
23 : return;
24 : }
25 : ... speculatively process account ...
26 : if( fd_accdb_peek_test( peek )!=FD_ACCDB_SUCCESS ) {
27 : ... data race detected ...
28 : return;
29 : }
30 : ... happy path ... */
31 :
32 : static inline fd_accdb_peek_t *
33 : fd_accdb_peek( fd_accdb_user_t * accdb,
34 : fd_accdb_peek_t * peek,
35 : fd_funk_txn_xid_t const * xid,
36 0 : void const * address ) {
37 0 : return accdb->base.vt->peek( accdb, peek, xid, address );
38 0 : }
39 :
40 : /* fd_accdb_peek_test verifies whether a previously taken peek still
41 : refers to valid account data. Returns 1 if still valid, 0 if peek
42 : may have seen a conflict. */
43 :
44 : FD_FN_PURE static inline int
45 3 : fd_accdb_peek_test( fd_accdb_peek_t const * peek ) {
46 3 : return fd_accdb_spec_test( peek->spec );
47 3 : }
48 :
49 : /* fd_accdb_peek_drop releases the caller's interest in the account. */
50 :
51 : static inline void
52 0 : fd_accdb_peek_drop( fd_accdb_peek_t * peek ) {
53 0 : fd_accdb_spec_drop( peek->spec );
54 0 : }
55 :
56 : FD_PROTOTYPES_END
57 :
58 : /* In-place read APIs *************************************************/
59 :
60 : static inline fd_accdb_ro_t *
61 : fd_accdb_open_ro( fd_accdb_user_t * accdb,
62 : fd_accdb_ro_t * ro,
63 : fd_funk_txn_xid_t const * txn_id,
64 0 : void const * address ) {
65 0 : return accdb->base.vt->open_ro( accdb, ro, txn_id, address );
66 0 : }
67 :
68 : static inline void
69 : fd_accdb_close_ro( fd_accdb_user_t * accdb,
70 0 : fd_accdb_ro_t * ro ) {
71 0 : accdb->base.vt->close_ro( accdb, ro );
72 0 : }
73 :
74 : /* FD_ACDB_RO_{BEGIN,END} provides RAII-style safe macros for
75 : fd_accdb_{open,close}_ro.
76 :
77 : Typical usage like:
78 :
79 : FD_ACCDB_RO_BEGIN( accdb, ro, xid, address ) {
80 : FD_LOG_NOTICE(( "Account has %lu lamports", fd_accdb_ref_lamports( ro ) ));
81 : }
82 : FD_ACCDB_RO_NOT_FOUND {
83 : FD_LOG_NOTICE(( "Account does not exist" ));
84 : }
85 : FD_ACCDB_RO_END; */
86 :
87 : struct fd_accdb_ro_scope_guard {
88 : fd_accdb_user_t * accdb;
89 : fd_accdb_ro_t * ro;
90 : };
91 : typedef struct fd_accdb_ro_scope_guard fd_accdb_ro_scope_guard_t;
92 :
93 : FD_FN_UNUSED static inline void
94 0 : fd_accdb_ro_scope_exit( fd_accdb_ro_scope_guard_t * guard ) {
95 0 : fd_accdb_close_ro( guard->accdb, guard->ro );
96 0 : }
97 :
98 : #define FD_ACCDB_RO_BEGIN( accdb__, handle, xid, address) \
99 0 : { \
100 0 : fd_accdb_ro_t handle[1]; \
101 0 : fd_accdb_user_t * accdb_ = (accdb__); \
102 0 : void const * addr_ = (address); \
103 0 : if( fd_accdb_open_ro( accdb, handle, (xid), addr_ ) ) { \
104 0 : fd_accdb_ro_scope_guard_t __attribute__((cleanup(fd_accdb_ro_scope_exit))) guard_ = \
105 0 : { .accdb=accdb_, .ro=handle }; \
106 0 : (void)guard_; \
107 0 : { \
108 : /* User-provided account found snippet */
109 : #define FD_ACCDB_RO_NOT_FOUND \
110 0 : } \
111 0 : } else { \
112 0 : { \
113 : /* User-provided account not found snippet */
114 : #define FD_ACCDB_RO_END \
115 0 : } \
116 0 : } \
117 0 : }
118 :
119 : /* In-place transactional write APIs **********************************/
120 :
121 : FD_PROTOTYPES_BEGIN
122 :
123 : /* fd_accdb_open_rw starts an account modification op. txn_xid names a
124 : non-rooted non-frozen fork graph node, and address identifies the
125 : account. If the account data buffer is smaller than data_max, it is
126 : resized (does not affect the data size, just the buffer capacity).
127 : If do_create==0, returns NULL if the account does not exist.
128 : Otherwise, returns an existing or newly created account.
129 :
130 : For the entire lifetime of an rw handle, the (txn_xid,address) pair
131 : MUST NOT be accessed by any other ro or rw operation. Violating this
132 : rule causes undefined behavior. The lifetime of an rw handle starts
133 : as soon as open_rw is called. It ends once all memory writes done
134 : after the close_rw call returns are visible to all other DB user
135 : threads.
136 :
137 : It is fine to do multiple open_rw/close_rw interactions with the same
138 : (txn_xid,address) pair assuming proper synchronization. Only the
139 : final state for a (txn_xid,address) pair is retained. */
140 :
141 : static inline fd_accdb_rw_t *
142 : fd_accdb_open_rw( fd_accdb_user_t * accdb,
143 : fd_accdb_rw_t * rw,
144 : fd_funk_txn_xid_t const * txn_id,
145 : void const * address,
146 : ulong data_max,
147 498 : int do_create ) {
148 498 : return accdb->base.vt->open_rw( accdb, rw, txn_id, address, data_max, do_create );
149 498 : }
150 :
151 : /* fd_accdb_close_rw publishes a previously prepared account write.
152 : Note that this function returns before memory writes have propagated
153 : to other threads, thus requires external synchronization. */
154 :
155 : static inline void
156 : fd_accdb_close_rw( fd_accdb_user_t * accdb,
157 0 : fd_accdb_rw_t * write ) { /* destroyed */
158 0 : accdb->base.vt->close_rw( accdb, write );
159 0 : }
160 :
161 : FD_PROTOTYPES_END
162 :
163 : #endif /* HEADER_fd_src_flamenco_accdb_fd_accdb_sync_h */
|